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\n", (unsigned int)pthread_self());
        return NULL;
    }
    char * p = (char*)arg;
    printf("[%u] : arg = [%s]\n", (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]\n", errn);
        if(EAGAIN == errn) {
            printf("Insufficient  resources\n");
        } else if (EINVAL == errn) {
            printf("Invalid settings in attr\n");
        } else if (EPERM == errn)  {
            printf("No permission\n");
        } else {
            printf("An error number that unexpected [%d], when create pthread\n", errn);
        }

        return -1;
    } else {
        printf("create thread success, threadid : [%u]\n", (unsigned int)pt);
    }
    void *r = NULL;
    errn = pthread_join(pt, &r);
    if(0 != errn) {
        printf("error happend when join, errno = [%d]\n", errn);
        if(EDEADLK == errn) {
            printf("A  deadlock  was  detected; or thread specifies the calling thread\n");
        } else if (EINVAL == errn) {
            printf("thread is not a joinable thread, or Another thread is already waiting to join with this thread\n");
        } else if (ESRCH == errn) {
            printf("No thread with the ID thread could be found\n");
        } else {
            printf("An error number that unexpected [%d], when join\n", errn);
        }
        return -1;
    } else {
        printf("thread [%u] over\n", (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]\n", __func__, (char*)arg);
    } else {
        printf("%s()\n", __func__);
    }
}

void *
thread_start(void *arg) {
    printf("hello, this is thrad [%u]\n", (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\n", errn);
        return -1;
    } else {
        printf("create thread success, threadid : [%u]\n", (unsigned int)pt);
    }
    void *r = NULL;
    errn = pthread_join(pt, &r);
    if(0 != errn) {
        printf("error happend when join, errno = [%d]\n", errn);
        return -1;
    } else {
        printf("thread [%u] over\n", (unsigned int)pt);
    }
    if(NULL != r) {
        printf("thread return : [%s]\n", (const char*)r);
    }

    return 0;
}

编译并运行:

#include <stdio.h>
#include <pthread.h>

void handlers(void *arg) {
    if(NULL != arg) {
        printf("%s() : [%s]\n", __func__, (char*)arg);
    } else {
        printf("%s()\n", __func__);
    }
}

void *
thread_start(void *arg) {
    printf("hello, this is thrad [%u]\n", (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\n", errn);
        return -1;
    } else {
        printf("create thread success, threadid : [%u]\n", (unsigned int)pt);
    }
    void *r = NULL;
    errn = pthread_join(pt, &r);
    if(0 != errn) {
        printf("error happend when join, errno = [%d]\n", errn);
        return -1;
    } else {
        printf("thread [%u] over\n", (unsigned int)pt);
    }
    if(NULL != r) {
        printf("thread return : [%s]\n", (const char*)r);
    }

    return 0;
}

使用 return 退出线程

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

#include <stdio.h>
#include <pthread.h>

void *
thread_start(void *arg) {
    printf("hello, this is thread [%u]\n", (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\n", errn);
        return -1;
    } else {
        printf("create thread success, threadid : [%u]\n", (unsigned int)pt);
    }
    void *r = NULL;
    errn = pthread_join(pt, &r);
    if(0 != errn) {
        printf("error happend when join, errno = [%d]\n", errn);
        return -1;
    } else {
        printf("thread [%u] over\n", (unsigned int)pt);
    }
    if(NULL != r) {
        printf("thread return : [%s]\n", (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]\n", __func__, (char*)arg);
    } else {
        printf("%s()\n", __func__);
    }
}

void *
thread_start(void *arg) {
    printf("hello, this is thrad [%u]\n", (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\n", errn);
        return -1;
    } else {
        printf("create thread success, threadid : [%u]\n", (unsigned int)pt);
    }
    void *r = NULL;
    errn = pthread_join(pt, &r);
    if(0 != errn) {
        printf("error happend when join, errno = [%d]\n", errn);
        return -1;
    } else {
        printf("thread [%u] over\n", (unsigned int)pt);
    }
    if(NULL != r) {
        printf("thread return : [%s]\n", (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\n");
    sleep(5);
    printf("thread_func(): about to enable cancellation\n");

    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!\n");
    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\n");
    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\n");
    else
        printf("main(): thread wasn‘t canceled (shouldn‘t happen!)\n");
    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

时间: 2024-10-03 06:34:23

POSIX 线程的创建与退出的相关文章

LINUX多线程(一)(创建和退出)

1. Linux多线程概述 1.1. 概述 进程是系统中程序执行和资源分配的基本单位.每个进程有自己的数据段.代码段和堆栈段.这就造成进程在进行切换等操作时都需要有比较负责的上下文切换等动作.为了进一步减少处理器的空转时间支持多处理器和减少上下文切换开销,也就出现了线程. 线程通常叫做轻量级进程.线程是在共享内存空间中并发执行的多道执行路径,是一个更加接近于执行体的概念,拥有独立的执行序列,是进程的基本调度单元,每个进程至少都有一个main线程.它与同进程中的其他线程共享进程空间{堆 代码 数据

POSIX 线程详解(2-线程创建和销毁)

算法旨在用尽可能简单的思路解决问题,理解算法也应该是一个越看越简单的过程,当你看到算法里的一串概念,或者一大坨代码,第一感觉是复杂,此时不妨从例子入手,通过一个简单的例子,并编程实现,这个过程其实就可以理解清楚算法里的最重要的思想,之后扩展,对算法的引理或者更复杂的情况,对算法进行改进.最后,再考虑时间和空间复杂度的问题. 了解这个算法是源于在Network Alignment问题中,图论算法用得比较多,而对于alignment,特别是pairwise alignment, 又经常遇到maxim

进程线程创建与退出监视(DBGVIEW打印)

曾经想着做个自己的主动防御项目,就是监控而已,实现RING3通信,各方参考学习,想着HOOK 来着 但是有一次知道了无HOOK的方式,感觉这挺方便呀 还是微软承认的函数,那我就实现一套监控的方案,考虑到逆向分析的话就准备后面 有事没事空闲了就去尽量实现HOOK的方式,希望一切顺利,少蓝屏几次.谢谢胡老师教程指导! 这里是代码和注意:实现的是监控进程线程创建退出,如果calc创建则阻止创建(W7 64位通过) 其中注意的是: 进程监视函数 PsSetCreateProcessNotifyRouti

POSIX 线程详解(经典必看)

总共三部分: 第一部分:POSIX 线程详解                                   Daniel Robbins ([email protected]), 总裁/CEO, Gentoo Technologies, Inc.  2000 年 7 月 01 日 第二部分:通用线程:POSIX 线程详解,第 2部分       Daniel Robbins ([email protected]), 总裁/CEO, Gentoo Technologies, Inc.  20

Posix线程编程指南(1)

Posix线程编程指南(1) 作者:杨沙洲 原文地址:http://www.ibm.com/developerworks/cn/linux/thread/posix_threadapi/part1/ 线程创建与取消 这是一个关于Posix线程编程的专栏.作者在阐明概念的基础上,将向您详细讲述Posix线程库API.本文是第一篇将向您讲述线程的创建与取消. 1 线程创建 1.1 线程与进程 相对进程而言,线程是一个更加接近于执行体的概念,它可以与同进程中的其他线程共享数据,但拥有自己的栈空间,拥有

Linux C线程的创建和使用 [转]

1 引言 线程(thread)技术早在60年代就被提出,但真正应用多线程到操作系统中 去,是在80年代中期,solaris是这方面的佼佼者.传统的Unix也支持线程的概念,但是在一个进程(process)中只允许有一个线程,这样多 线程就意味着多进程.现在,多线程技术已经被许多操作系统所支持,包括Windows/NT,当然,也包括Linux. 为什么有了进程的概念后,还要再引入线程呢?使用多线程到底有哪些好处?什么的系统应该选用多线程?我们首先必须回答这些问题. 使用多线程的理由之一是和进程相比

POSIX 线程(一)

一. POSIX线程先关函数 POSIX线程库 与线程有关的函数构成一个完整的系列,绝大多数函数的名字都是以"pthread"打头的 要使用这些函数库,引入头文件<pthread.h> 连接这些线程函数库时要使用编译器命令的"-lpthread"选项 1. pthread_create函数 功能:创建一个新的线程 原型: int pthread_create(pthread_t *thread, const pthread_attr_t *attr, v

POSIX线程(二)

一. 线程属性 (1) 初始化与销毁属性 int pthread_attr_init(pthread_attr_t *attr); int pthread_attr_destroy(pthread_attr_t *attr); (2)获取与设置分离属性 int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); int pthread_attr_getdetachstate(pthread_attr_t *att

Posix线程编程指南(2)

Posix线程编程指南(2) 杨沙洲 原文地址:http://www.ibm.com/developerworks/cn/linux/thread/posix_threadapi/part2/ 线程私有数据 这是一个关于Posix线程编程的专栏.作者在阐明概念的基础上,将向您详细讲述Posix线程库API.本文是第二篇将向您讲述线程的私有数据. 概念及作用 在单线程程序中,我们经常要用到"全局变量"以实现多个函数间共享数据.在多线程环境下,由于数据空间是共享的,因此全局变量也为所有线程