zoukankan      html  css  js  c++  java
  • POSIX 线程的创建与退出

    前言

    创建线程:

    pthread_create()

    退出线程:

    pthread_exit()return
    pthread_cancel()

    线程的创建

    使用多线程,首先就需要创建一个新线程。那么线程是如何被创建的呢,是用下面这个函数创建的。

    #include <pthread.h>
    
    int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                       void *(*start_routine) (void *), void *arg);
    
    //Compile and link with -pthread
     

    创建函数的四个参数的意义分别如下:

    thread :用来返回新创建的线程的 ID,这个 ID 就像身份证一样,指定了这个线程,可以用来在随后的线程交互中使用。
    
    attr   : 这个参数是一个 pthread_attr_t 结构体的指针,用来在线程创建的时候指定新线程的属性。如果在创建线程时,这个参数指定为 NULL, 那么就会使用默认属性。
    
    start_routine :这个就是新线程的入口函数,当新线程创建完成后,就从这里开始执行。
    
    arg :arg 参数就是要传递给 start_routine 的参数。

    返回值:如果函数执行成功,则返回 0,如果执行失败,则返回一个错误码。

    错误码:

        EAGAIN :资源不足以用来创建一个新的线程,或者是达到了系统对线程数量的限制,请参考 setrlimit(2) 和 /proc/sys/kernel/threads-max 
        EINVAL :不可用的 attr 
        EPERM  :没有权限设置 attr 中的一下属性或者执行时序策略。

    下面就是调用 pthread_create() 函数创建线程的一个例子:

    #include <stdio.h>
    #include <pthread.h>
    #include <errno.h>
    
    void *
    thread_start(void *arg) {
        if(NULL == arg) {
            printf("[%u] : arg is NULL
    ", (unsigned int)pthread_self());
            return NULL;
        }
        char * p = (char*)arg;
        printf("[%u] : arg = [%s]
    ", (unsigned int)pthread_self(), p);
    
        return NULL;
    }
    
    int main() {
        pthread_t pt;
        int errn = pthread_create(
            &pt,         //用来返回新创建的线程的 ID
            NULL,        //使用默认的线程属性
            thread_start,//新线程从这个函数开始执行
            "hello");    //传递给新创建的线程的参数
    
        if(0 != errn) {
            printf("error happend when create pthread, errno = [%d]
    ", errn);
            if(EAGAIN == errn) {
                printf("Insufficient  resources
    ");
            } else if (EINVAL == errn) {
                printf("Invalid settings in attr
    ");
            } else if (EPERM == errn)  {
                printf("No permission
    ");
            } else {
                printf("An error number that unexpected [%d], when create pthread
    ", errn);
            }
    
            return -1;
        } else {
            printf("create thread success, threadid : [%u]
    ", (unsigned int)pt);
        }
        void *r = NULL;
        errn = pthread_join(pt, &r);
        if(0 != errn) {
            printf("error happend when join, errno = [%d]
    ", errn);
            if(EDEADLK == errn) {
                printf("A  deadlock  was  detected; or thread specifies the calling thread
    ");
            } else if (EINVAL == errn) {
                printf("thread is not a joinable thread, or Another thread is already waiting to join with this thread
    ");
            } else if (ESRCH == errn) {
                printf("No thread with the ID thread could be found
    ");
            } else {
                printf("An error number that unexpected [%d], when join
    ", errn);
            }
            return -1;
        } else {
            printf("thread [%u] over
    ", (unsigned int)pt);
        }
    
        return 0;
    }

    接下来编译并运行,看看结果:

    gcc -g -c -o pthread_create.o pthread_create.c -Wall -I./
    gcc -g -o pthread_create pthread_create.o -Wall -I./ -lpthread
    
    create thread success, threadid : [1243236096]
    [1243236096] : arg = [hello]
    thread [1243236096] over

    看起来执行成功了。下面再来看看一个线程的退出过程。

    线程的退出

    从上面的例子中,我们也可以看出,线程的入口,也就是一个函数,函数可以使用 return 进行退出, 那么在线程中,也是通过 return 进行退出的吗? 答案是,可以使用 return ,但是如果希望线程在退出的时候, 能够执行更多的动作,就不能使用 return 直接退出了,那么该怎样退出呢,可以使用 pthread_exit() 函数, 或者使用pthread_cancel() 函数。

    这两个函数的原型如下:

    #include <pthread.h>
    
    int pthread_cancel(pthread_t thread);   //向指定的线程发送取消请求
    void pthread_exit(void *retval);        //结束调用者线程
    
    //Compile and link with -pthread

    使用 pthread_exit() 退出线程

    pthread_exit() 函数会结束当前进程。如果当前线程是可以被 join 的,则会通过参数 retval 返回一个值给同一个进程里面的另一个使用pthread_join(3) 函数的线程。

    所有使用pthread_cleanup_push(3)函数压入栈的清理函数,都会被弹出并调用, 调用顺序是入栈时的反向顺序。如果线程有什么特别指定的数据,那么在所有的清理函数执行结束后, 会有适当的函数被调用,来析构这些数据,调用顺序不固定。

    当一个线程终止后,进程内共享的资源(例如互斥信号量、条件变量、信号量以及文件描述符) 不会被释放。并且使用atexit(3)函数注册的函数也不会被调用。

    当进程内的最后一个线程终止后,进程也就终止了,就像调用了exit(3)函数一样,并且参数是0. 这时候,进程内的共享资源就会被释放,并且使用 atexit(3) 函数注册的函数, 也会被调用。

    下面来看一下 pthread_exit() 函数的一个例子:

    #include <stdio.h>
    #include <pthread.h>
    
    void handlers(void *arg) {
        if(NULL != arg) {
            printf("%s() : [%s]
    ", __func__, (char*)arg);
        } else {
            printf("%s()
    ", __func__);
        }
    }
    
    void *
    thread_start(void *arg) {
        printf("hello, this is thrad [%u]
    ", (unsigned int)pthread_self());
        pthread_cleanup_push(handlers, "one");
        pthread_cleanup_push(handlers, "two");
        pthread_cleanup_push(handlers, "three");
    
        //注意,这里执行了 pthread_exit() 函数
        pthread_exit("he~he~");
    
        pthread_cleanup_pop(1);
        pthread_cleanup_pop(2);
        pthread_cleanup_pop(3);
    
        return NULL;
    }
    
    int main() {
        pthread_t pt;
        int errn = pthread_create(&pt, NULL, thread_start, NULL);
    
        if(0 != errn) {
            printf("error [%d], when create pthread
    ", errn);
            return -1;
        } else {
            printf("create thread success, threadid : [%u]
    ", (unsigned int)pt);
        }
        void *r = NULL;
        errn = pthread_join(pt, &r);
        if(0 != errn) {
            printf("error happend when join, errno = [%d]
    ", errn);
            return -1;
        } else {
            printf("thread [%u] over
    ", (unsigned int)pt);
        }
        if(NULL != r) {
            printf("thread return : [%s]
    ", (const char*)r);
        }
    
        return 0;
    }

    编译并运行:

    #include <stdio.h>
    #include <pthread.h>
    
    void handlers(void *arg) {
        if(NULL != arg) {
            printf("%s() : [%s]
    ", __func__, (char*)arg);
        } else {
            printf("%s()
    ", __func__);
        }
    }
    
    void *
    thread_start(void *arg) {
        printf("hello, this is thrad [%u]
    ", (unsigned int)pthread_self());
        pthread_cleanup_push(handlers, "one");
        pthread_cleanup_push(handlers, "two");
        pthread_cleanup_push(handlers, "three");
    
        //注意,这里执行了 pthread_exit() 函数
        pthread_exit("he~he~");
    
        pthread_cleanup_pop(1);
        pthread_cleanup_pop(2);
        pthread_cleanup_pop(3);
    
        return NULL;
    }
    
    int main() {
        pthread_t pt;
        int errn = pthread_create(&pt, NULL, thread_start, NULL);
    
        if(0 != errn) {
            printf("error [%d], when create pthread
    ", errn);
            return -1;
        } else {
            printf("create thread success, threadid : [%u]
    ", (unsigned int)pt);
        }
        void *r = NULL;
        errn = pthread_join(pt, &r);
        if(0 != errn) {
            printf("error happend when join, errno = [%d]
    ", errn);
            return -1;
        } else {
            printf("thread [%u] over
    ", (unsigned int)pt);
        }
        if(NULL != r) {
            printf("thread return : [%s]
    ", (const char*)r);
        }
    
        return 0;
    }

    使用 return 退出线程

    先来看一个使用 return 退出线程的例子:

    #include <stdio.h>
    #include <pthread.h>
    
    void *
    thread_start(void *arg) {
        printf("hello, this is thread [%u]
    ", (unsigned int)pthread_self());
    
        return "ok";
    }
    
    int main() {
        pthread_t pt;
        int errn = pthread_create(&pt, NULL, thread_start, NULL);
    
        if(0 != errn) {
            printf("error [%d], when create pthread
    ", errn);
            return -1;
        } else {
            printf("create thread success, threadid : [%u]
    ", (unsigned int)pt);
        }
        void *r = NULL;
        errn = pthread_join(pt, &r);
        if(0 != errn) {
            printf("error happend when join, errno = [%d]
    ", errn);
            return -1;
        } else {
            printf("thread [%u] over
    ", (unsigned int)pt);
        }
        if(NULL != r) {
            printf("thread return : [%s]
    ", (const char*)r);
        }
    
        return 0;
    }

    编译并运行:

    gcc -g -c -o pthread-return.o pthread-return.c -Wall -I./
    gcc -g -o pthread-return pthread-return.o -Wall -I./ -lpthread
    
    ./pthread-return 
    create thread success, threadid : [722429696]   //主线程打印的信息
    hello, this is thread [722429696]               //新创建的线程打印的信息
    thread [722429696] over                         //主线程打印的信息
    thread return : [ok]                            //主线程打印的信息,其中[ok]为新创建的线程打印的信息

    既然 return 和 pthread_exit() 函数都是结束线程,并返回数据,那么它们之间的区别是什么呢?

    区别就在于,使用 return 退出线程的时候,不会执行线程使用 pthread_cleanup_push(3) 注册的清理函数。 可以再写一个例子,看看效果。

    #include <stdio.h>
    #include <pthread.h>
    
    void handlers(void *arg) {
        if(NULL != arg) {
            printf("%s() : [%s]
    ", __func__, (char*)arg);
        } else {
            printf("%s()
    ", __func__);
        }
    }
    
    void *
    thread_start(void *arg) {
        printf("hello, this is thrad [%u]
    ", (unsigned int)pthread_self());
        pthread_cleanup_push(handlers, "one");
        pthread_cleanup_push(handlers, "two");
        pthread_cleanup_push(handlers, "three");
    
        //注意,这里执行了 return
        return "he~he~";
    
        pthread_cleanup_pop(1);
        pthread_cleanup_pop(2);
        pthread_cleanup_pop(3);
    
        return "ok";
    }
    
    int main() {
        pthread_t pt;
        int errn = pthread_create(&pt, NULL, thread_start, NULL);
    
        if(0 != errn) {
            printf("error [%d], when create pthread
    ", errn);
            return -1;
        } else {
            printf("create thread success, threadid : [%u]
    ", (unsigned int)pt);
        }
        void *r = NULL;
        errn = pthread_join(pt, &r);
        if(0 != errn) {
            printf("error happend when join, errno = [%d]
    ", errn);
            return -1;
        } else {
            printf("thread [%u] over
    ", (unsigned int)pt);
        }
        if(NULL != r) {
            printf("thread return : [%s]
    ", (const char*)r);
        }
    
        return 0;
    }

    编译并运行:

    gcc -g -c -o pthread-return.o pthread-return.c -Wall -I./
    gcc -g -o pthread-return pthread-return.o -Wall -I./ -lpthread
    
    ./pthread-return 
    create thread success, threadid : [4185097984]
    hello, this is thrad [4185097984]
    thread [4185097984] over
    thread return : [he~he~]

    可以看出,确实没有执行清理函数,为什么呢?

    因为pthread_cleanup_push(3) 和 pthread_cleanup_pop() 是使用宏实现的。 在 pthread_cleanup_push() 和 pthread_cleanup_pop() 之间,是一个大个的 do{}while(0), 遇到 return 当然就直接退出啦。 具体的实现方式请看这里, 因为本文只讲述一下线程的创建和退出, 所以 pthread_cleanup_push 和 pthread_cleanup_pop 的说明放在其它地方了。

    使用 pthread_cancel() 退出线程

    先看一下函数原型:

    #include <pthread.h>
    
    int pthread_cancel(pthread_t thread);
    
    //Compile and link with -pthread.

    其中的 thread 参数就是目的线程的线程ID

    pthread_cancel() 函数会给 thread 指定的线程发送一个取消请求。 至于目标线程是否以及合适对这个请求进行反应,则视目标线程的两个属性而定: 取消属性的 state 和 type

    一个线程的取消属性的 state 由 pthread_setcancelstate(3) 函数来设置, 可以是 enabled (一个新创建的线程的默认方式就是 enabled)或者 disabled。 如果一个线程的取消属性设置了 disabled ,那么对着个线程发送的取消请求会一直存在, 直到线程恢复了取消属性的设置。如果一个线程的取消属性设置了 enabled , 那么取消属性的 type 就由取消消息什么什么时候到来而决定了。

    一个线程的取消类型(type)由 pthread_setcanceltype(3) 函数来设置。 可以是异步的,也可以是延缓的。异步取消属性的意味着线程任何时间都可以被取消 (通常是立即被取消,但操作系统不保证这一点)。延缓取消是说,取消操作会被延迟, 直到线程接下来的调用的函数是个取消点。在 pthreads(7) (Linux 命令行中执行 man 7 pthreads) 中列出的函数就是或者是取消点。

    当一个取消请求起作用时,下面的步骤会按顺序发生。

      1. 取消清理函数会被出栈并被执行。
      1. 线程相关数据会被析构,顺序不确定。
      1. 线程终止。

    以上的步骤会异步的执行,pthread_cancel() 函数的返回状态会指出取消请求是否成功的发给了制定的线程。

    在一个被取消的线程终止后,使用 pthread_join(3) 函数 join 时,会得到线程的结束状态为 PTHREAD_CANCELED 。 join 一个线程是知道这个取消操作是否完成的唯一方法。

    下面是 man pthread_cancel 手册中的一段示例代码:

    #include <pthread.h>
    #include <stdio.h>
    #include <errno.h>
    #include <stdlib.h>
    #include <unistd.h>
    
    #define handle_error_en(en, msg) 
        do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)
    
        static void *
    thread_func(void *ignored_argument)
    {
        int s;
    
        /*  Disable cancellation for a while, so that we don't
         *                immediately react to a cancellation request */
    
        s = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
        if (s != 0)
            handle_error_en(s, "pthread_setcancelstate");
    
        printf("thread_func(): started; cancellation disabled
    ");
        sleep(5);
        printf("thread_func(): about to enable cancellation
    ");
    
        s = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
        if (s != 0)
            handle_error_en(s, "pthread_setcancelstate");
    
        /*  sleep() is a cancellation point */
    
        sleep(1000);        /*  Should get canceled while we sleep */
    
        /*  Should never get here */
    
        printf("thread_func(): not canceled!
    ");
        return NULL;
    }
    
        int
    main(void)
    {
        pthread_t thr;
        void *res;
        int s;
    
        /*  Start a thread and then send it a cancellation request */
    
        s = pthread_create(&thr, NULL, &thread_func, NULL);
        if (s != 0)
            handle_error_en(s, "pthread_create");
    
        sleep(2);           /*  Give thread a chance to get started */
    
        printf("main(): sending cancellation request
    ");
        s = pthread_cancel(thr);
        if (s != 0)
            handle_error_en(s, "pthread_cancel");
    
        /*  Join with thread to see what its exit status was */
    
        s = pthread_join(thr, &res);
        if (s != 0)
            handle_error_en(s, "pthread_join");
    
        if (res == PTHREAD_CANCELED)
            printf("main(): thread was canceled
    ");
        else
            printf("main(): thread wasn't canceled (shouldn't happen!)
    ");
        exit(EXIT_SUCCESS);
    }

    编译并运行 :

    ./pthread_cancel 
    thread_func(): started; cancellation disabled
    main(): sending cancellation request
    thread_func(): about to enable cancellation
    main(): thread was canceled

    同步地址:https://www.fengbohello.top/archives/linux-pthread-lifecycle

  • 相关阅读:
    hdu5360 Hiking(水题)
    hdu5348 MZL's endless loop(欧拉回路)
    hdu5351 MZL's Border(规律题,java)
    hdu5347 MZL's chemistry(打表)
    hdu5344 MZL's xor(水题)
    hdu5338 ZZX and Permutations(贪心、线段树)
    hdu 5325 Crazy Bobo (树形dp)
    hdu5323 Solve this interesting problem(爆搜)
    hdu5322 Hope(dp)
    Lightoj1009 Back to Underworld(带权并查集)
  • 原文地址:https://www.cnblogs.com/fengbohello/p/7583630.html
Copyright © 2011-2022 走看看