【操作系统】线程常用操作

线程号

就像每个进程都有一个进程号一样,每个线程也有一个线程号。进程号在整个系统中是唯一的,但线程号不同,线程号只在它所属的进程环境中有效。

进程号用 pid_t 数据类型表示,是一个非负整数。线程号则用 pthread_t 数据类型来表示,Linux 使用无符号长整数表示。

有的系统在实现pthread_t 的时候,用一个结构体来表示,所以在可移植的操作系统实现不能把它做为整数处理。

pthread_self函数:

 #include 
 pthread_t pthread_self(void);
 功能:
     获取线程号。
 参数
     无
 返回值:
     调用线程的线程 ID 。

线程的创建

pthread_create函数:

 #include 
 int pthread_create(pthread_t *thread,
             const pthread_attr_t *attr,
             void *(*start_routine)(void *),
             void *arg );
 功能:
     创建一个线程。
 参数:
     thread:线程标识符地址。
     attr:线程属性结构体地址,通常设置为 NULL。
     start_routine:线程函数的入口地址。
     arg:传给线程函数的参数。
 返回值:
     成功:0
     失败:非 0

在一个线程中调用pthread_create()创建新的线程后,当前线程从pthread_create()返回继续往下执行,而新的线程所执行的代码由我们传给pthread_create的函数指针start_routine决定。

由于pthread_create的错误码不保存在errno中,因此不能直接用perror()打印错误信息,可以先用strerror()把错误码转换成错误信息再打印。

参考程序:

 // 回调函数
 void *thread_fun(void * arg)
 {
     sleep(1);
     int num = *((int *)arg);
     printf("int the new thread: num = %d\n", num);
 ​
     return NULL;
 }
 ​
 int main()
 {
     pthread_t tid;
     int test = 100;
 ​
     // 返回错误号
     int ret = pthread_create(&tid, NULL, thread_fun, (void *)&test);
     if (ret != 0)
     {
         printf("error number: %d\n", ret);
         // 根据错误号打印错误信息
         printf("error information: %s\n", strerror(ret));
     }
 ​
     while (1);
 ​
     return 0;
 }
 ​
 ​

线程资源回收

pthread_join函数:

 #include 
 ​
 int pthread_join(pthread_t thread, void **retval);
 功能:
     等待线程结束(此函数会阻塞),并回收线程资源,类似进程的 wait() 函数。如果线程已经结束,那么该函数会立即返回。
 参数:
     thread:被等待的线程号。
     retval:用来存储线程退出状态的指针的地址。
 返回值:
     成功:0
     失败:非 0

参考程序:

 void *thead(void *arg)
 {
     static int num = 123; //静态变量
 ​
     printf("after 2 seceonds, thread will return\n");
     sleep(2);
 ​
     return #
 }
 ​
 int main()
 {
     pthread_t tid;
     int ret = 0;
     void *value = NULL;
 ​
     // 创建线程
     pthread_create(&tid, NULL, thead, NULL);
 ​
 ​
     // 等待线程号为 tid 的线程,如果此线程结束就回收其资源
     // &value保存线程退出的返回值
     pthread_join(tid, &value);
 ​
     printf("value = %d\n", *((int *)value));
 ​
     return 0;
 }

调用该函数的线程将挂起等待,直到id为thread的线程终止。thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的,总结如下:

  1. 如果thread线程通过return返回,retval所指向的单元里存放的是thread线程函数的返回值。

  2. 如果thread线程被别的线程调用pthread_cancel异常终止掉,retval所指向的单元里存放的是常数PTHREAD_CANCELED。

  3. 如果thread线程是自己调用pthread_exit终止的,retval所指向的单元存放的是传给pthread_exit的参数。

线程分离

一般情况下,线程终止后,其终止状态一直保留到其它线程调用pthread_join获取它的状态为止。但是线程也可以被置为detach状态,这样的线程一旦终止就立刻回收它占用的所有资源,而不保留终止状态。

不能对一个已经处于detach状态的线程调用pthread_join,这样的调用将返回EINVAL错误。也就是说,如果已经对一个线程调用了pthread_detach就不能再调用pthread_join了。

pthread_detach函数:

  
 #include 
 ​
 int pthread_detach(pthread_t thread);
 功能:
     使调用线程与当前进程分离,分离后不代表此线程不依赖与当前进程,线程分离的目的是将线程资源的回收工作交由系统自动来完成,也就是说当被分离的线程结束之后,系统会自动回收它的资源。所以,此函数不会阻塞。
 参数:
     thread:线程号。
 返回值:
     成功:0
     失败:非0

线程退出

在进程中我们可以调用exit函数或_exit函数来结束进程,在一个线程中我们可以通过以下三种在不终止整个进程的情况下停止它的控制流。

  • 线程从执行函数中返回。

  • 线程调用pthread_exit退出线程。

  • 线程可以被同一进程中的其它线程取消。

pthread_exit函数:

 #include 
 ​
 void pthread_exit(void *retval);
 功能:
     退出调用线程。一个进程中的多个线程是共享该进程的数据段,因此,通常线程退出后所占用的资源并不会释放。
 参数:
     retval:存储线程退出状态的指针。
 返回值:无  

参考程序:

 void *thread(void *arg)
 {
     static int num = 123; //静态变量
     int i = 0;
     while (1)
     {
         printf("I am runing\n");
         sleep(1);
         i++;
         if (i == 3)
         {
             pthread_exit((void *)&num);
             // return #
         }
     }
 ​
     return NULL;
 }
 ​
 int main(int argc, char *argv[])
 {
     int ret = 0;
     pthread_t tid;
     void *value = NULL;
 ​
     pthread_create(&tid, NULL, thread, NULL);
 ​
 ​
     pthread_join(tid, &value);
     printf("value = %d\n", *(int *)value);
 ​
     return 0;
 }

线程取消

#include

int pthread_cancel(pthread_t thread);
功能:
    杀死(取消)线程
参数:
    thread : 目标线程ID。
返回值:
    成功:0
    失败:出错编号

注意:线程的取消并不是实时的,而又一定的延时。需要等待线程到达某个取消点(检查点)。

类似于玩游戏存档,必须到达指定的场所(存档点,如:客栈、仓库、城里等)才能存储进度。

杀死线程也不是立刻就能完成,必须要到达取消点。

取消点:是线程检查是否被取消,并按请求进行动作的一个位置。通常是一些系统调用creat,open,pause,close,read,write..... 执行命令**man 7 pthreads**可以查看具备这些取消点的系统调用列表。

可粗略认为一个系统调用(进入内核)即为一个取消点。

参考程序:

```
void *thread_cancel(void *arg)
{
    while (1)
    {
        pthread_testcancel(); //设置取消点
    }
    return NULL;
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, NULL, thread_cancel, NULL); //创建线程

    sleep(3);                   //3秒后
    pthread_cancel(tid); //取消tid线程

    pthread_join(tid, NULL);

    return 0;
}

```

 线程取消

```
#include

int pthread_cancel(pthread_t thread);
功能:
    杀死(取消)线程
参数:
    thread : 目标线程ID。
返回值:
    成功:0
    失败:出错编号

```

注意:线程的取消并不是实时的,而又一定的延时。需要等待线程到达某个取消点(检查点)。

类似于玩游戏存档,必须到达指定的场所(存档点,如:客栈、仓库、城里等)才能存储进度。

杀死线程也不是立刻就能完成,必须要到达取消点。

取消点:是线程检查是否被取消,并按请求进行动作的一个位置。通常是一些系统调用creat,open,pause,close,read,write..... 执行命令**man 7 pthreads**可以查看具备这些取消点的系统调用列表。

可粗略认为一个系统调用(进入内核)即为一个取消点。

参考程序:

```
void *thread_cancel(void *arg)
{
    while (1)
    {
        pthread_testcancel(); //设置取消点
    }
    return NULL;
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, NULL, thread_cancel, NULL); //创建线程

    sleep(3);                   //3秒后
    pthread_cancel(tid); //取消tid线程

    pthread_join(tid, NULL);

    return 0;
}

```

你可能感兴趣的:(java,jvm,linux)