linux线程的创建与删除

linux线程的创建与删除

使用linux线程时,编译时需要包含-pthread选项。

Linux通用API返回0表示成功,返回-1表示失败,并设置errno以标识错误原因。但Pthreads相关的API却与之不同,返回0 表示成功,返回正值表示失败,正值的含义与errno相同。

我们可以使用pthread_create()来创建线程。创建成功后的线程可以使用如下几种方式终止:

  1. 线程主函数中执行return语句并返回指定值。
  2. 调用pthread_exit()函数终止线程
  3. 调用pthread_cancel()取消线程。
  4. 任意线程调用了exit(),或者主线程执行了return语句(在main()函数中),都会导致进程中的所有线程立即终止。

当线程终止退出时,分两种情况:

  1. 线程为可连接(joinable)状态。这也是线程的默认状态。这种状态的线程退出时,必须使用pthread_join()函数连接。用于资源回收。否则会产生相当于僵尸进程的线程。除了浪费系统资源以外,僵尸线程若累积过多,应用将再也无法创建新的线程。
  2. 线程为分离(detached)状态。调用pthread_detach()将该线程标记为处于分离(detached)状态。一旦线程被分离,就不可能再使用pthread_join()来获得它的返回状态,并且该线程也不能再次连接。这种情况一般用于程序员并不关心线程的返回状态,只是希望系统在线程终止时能够自动清理并移除之。

线程使用相关的函数API:

  1. int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg)
    1. 创建一个线程
    2. thread:用于保存线程标识符的实体指针。
    3. attr:用于指定要创建线程的各种属性。
    4. start_routine:线程实体函数
    5. arg:线程实体函数的参数。
    6. return:
  2. void pthread_exit(void *retval)
    1. 终止当前线程。
    2. retval:指定线程的返回内容。pthread_join()可获取返回的内容。retval内容不能是局部变量。必须为全局变量等。
  3. pthread_t pthread_self(void)
    1. 获取当前线程的id.
  4. int pthread_equal(pthread_t t1, pthread_t t2)
    1. 检查两个线程是否相等。
    2. return:if true:返回非0值。if false:返回0.
  5. int pthread_join(pthread_t thread, void **retval)
    1. 等待某个指定的线程终止。
    2. thread:指定要等待终止的线程id.
    3. retval:用于接收线程终止后返回的内容。
    4. return:if true:0. if false:大于0的错误码。
    5. 注意:当线程终止时,必须使用pthread_join()函数连接。否则会产生相当于僵尸进程的线程。除了浪费系统资源以外,僵尸线程若累积过多,应用将再也无法创建新的线程。
    6. 注意:线程之间的关系是对等的。即没有进程中的父子关系等。因此进程中的任意线程均可以调用pthread_join()与该进程的任何其他线程连接起来。
  6. int pthread_detach(pthread_t thread)
    1. 默认情况下,线程是可连接的(joinable),也就是说,当线程退出时,其他线程可以通过调用pthread_join()获取其返回状态。有时,程序员并不关心线程的返回状态,只是希望系统在线程终止时能够自动清理并移除之。在这种情况下,可以调用pthread_detach()并向thread 参数传入指定线程的标识符,将该线程标记为处于分离(detached)状态。一旦线程被分离,就不可能再使用pthread_join()来获得它的返回状态,并且该线程也不能再次连接。
    2. thread:线程id
    3. return:if true:0. if false:大于0的错误码。

如下是一个简单的线程创建例程:

#include 
#include 
#include 
#include 
#include 

void *thread_entry(void *arg)
{
    int num = 0;
    int count = *((int *)arg);

    for (int i = 0; i < count; i++){
        num ++;
    }

    return NULL;
}

void main(void)
{
    // 1. 定义线程描述符
    pthread_t t1;
    int counts = 20000;

    // 2. 创建线程
    if(pthread_create(&t1, NULL, thread_entry, &counts) != 0){
        printf("thread t1 create error.");
        exit(1);
    }

    // 3. 回收线程资源
    if(pthread_join(t1, NULL) != 0){
        printf("pthread_join t1 error.");
        exit(1);
    }

    exit(0);
}

线程取消

上面提到的线程终止函数pthread_exit()用于自身线程的终止。但有时也需要其他线程来终止当前线程。这时就涉及到线程取消了。

线程取消:其他线程向本线程发送一个请求,要求本线程其立即退出。线程取消函数thread_cancel()只负责发送一个取消请求消息,然后立即返回。至于目标线程如何处理这个请求的,取决于目标线程的取消状态state和类型type

当因线程取消导致线程终止时,终止返回的内容为PTHREAD_CANCELED.即pthread_join()中第二个参数获取到的值将是一个特殊值:PTHREAD_CANCELED

  1. int pthread_cancel(pthread_t thread)
    1. 向目标线程发送取消请求。
    2. thread:目标线程id
    3. return:if true:0. if false:大于0的错误码。
  2. int pthread_setcancelstate(int state, int *oldstate)
    1. 设置当前线程的取消状态。
    2. state:要设置的状态,如下:
      1. PTHREAD_CANCEL_DISABLE:线程不可取消。如果此类线程收到取消请求,则会将请求挂起,直至将线程的取消状态置为启用。
      2. PTHREAD_CANCEL_ENABLE:线程可以取消。新建线程时的默认值。
    3. oldstate:用于返回之前的状态。
    4. return:if true:0. if false:大于0的错误码。
  3. int pthread_setcanceltype(int type, int *oldtype)
    1. 设置当前线程的取消类型。在取消状态设置为PTHREAD_CANCEL_ENABLE时使用。
    2. type:要设置的类型。
      1. PTHREAD_CANCEL_ASYNCHRONOUS:异步取消线程。即在线程的任何地方都可以取消。很少用。
      2. PTHREAD_CANCEL_DEFERED:取消请求保持挂起状态,直至到达取消点.新建线程时的默认值。
        1. 什么是取消点:简单说就是一些系统规定的可以取消线程的函数。当满足上述条件时(即state为使能,type为挂起),遇到这些函数线程就会终止。系统规定规定的取消点函数很多,如open()等各种系统调用。详见man手册。
    3. oldtype:用于返回之前的类型。
    4. return:if true:0. if false:大于0的错误码。
  4. void pthread_testcancel(void)
    1. 产生一个取消点。即如果线程中没有使用系统规定取消点函数。可以使用此函数来产生取消点,而不影响程序逻辑。

在线程取消时,可能会涉及到内存释放,互斥量恢复等操作。这时会涉及到线程清理相关的函数。

  1. void pthread_cleanup_push(void (*routine)(void*), void *arg)
    1. 将指定清理函数压栈。线程因取消请求进入终止状态时,会从栈顶向下依次运行栈中的清理函数。
    2. routine:清理函数
    3. arg:清理函数要传入的参数
  2. void pthread_cleanup_pop(int execute)
    1. 出栈。配合pthread_cleanup_push()使用。它俩必须属于同一语法块,必须一一对应。
    2. execute:执行状态。
      1. 0:正常出栈,不执行栈顶的清理函数。
      2. !0,出栈时,仍然会执行栈顶的清理函数。

关于技术交流

此处后的文字已经和题目内容无关,可以不看。
qq群:825695030
微信公众号:嵌入式的日常
如果上面的文章对你有用,欢迎打赏、点赞、评论。二维码

你可能感兴趣的:(初学linux,linux,线程使用,linux线程创建与删除)