前言

创建线程:

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() 和 /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( != 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 -;
} else {
printf("create thread success, threadid : [%u]\n", (unsigned int)pt);
}
void *r = NULL;
errn = pthread_join(pt, &r);
if( != 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 -;
} else {
printf("thread [%u] over\n", (unsigned int)pt);
} return ;
}

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

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

编译并运行:

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

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

编译并运行:

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 : [] //主线程打印的信息
hello, this is thread [] //新创建的线程打印的信息
thread [] 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();
pthread_cleanup_pop();
pthread_cleanup_pop(); return "ok";
} int main() {
pthread_t pt;
int errn = pthread_create(&pt, NULL, thread_start, NULL); if( != errn) {
printf("error [%d], when create pthread\n", errn);
return -;
} else {
printf("create thread success, threadid : [%u]\n", (unsigned int)pt);
}
void *r = NULL;
errn = pthread_join(pt, &r);
if( != errn) {
printf("error happend when join, errno = [%d]\n", errn);
return -;
} else {
printf("thread [%u] over\n", (unsigned int)pt);
}
if(NULL != r) {
printf("thread return : [%s]\n", (const char*)r);
} return ;
}

编译并运行:

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 : []
hello, this is thrad []
thread [] 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 () 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 != )
handle_error_en(s, "pthread_setcancelstate"); printf("thread_func(): started; cancellation disabled\n");
sleep();
printf("thread_func(): about to enable cancellation\n"); s = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
if (s != )
handle_error_en(s, "pthread_setcancelstate"); /* sleep() is a cancellation point */ sleep(); /* 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 != )
handle_error_en(s, "pthread_create"); sleep(); /* Give thread a chance to get started */ printf("main(): sending cancellation request\n");
s = pthread_cancel(thr);
if (s != )
handle_error_en(s, "pthread_cancel"); /* Join with thread to see what its exit status was */ s = pthread_join(thr, &res);
if (s != )
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

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

  1. Linux多线程编程——线程的创建与退出

    POSIX线程标准:该标准定义了创建和操纵线程的一整套API.在类Unix操作系统(Unix.Linux.Mac OS X等)中,都使用Pthreads作为操作系统的线程.Windows操作系统也有其 ...

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

    1. Linux多线程概述 1.1. 概述 进程是系统中程序执行和资源分配的基本单位.每个进程有自己的数据段.代码段和堆栈段.这就造成进程在进行切换等操作时都需要有比较负责的上下文切换等动作.为了进一 ...

  3. Posix线程编程指南(1) 线程创建与取消

    线程创建 1.1 线程与进程 相对进程而言,线程是一个更加接近于执行体的概念,它可以与同进程中的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列.在串行程序基础上引入线程和进程是为了提高程序的 ...

  4. Win64 驱动内核编程-12.回调监控进线程创建和退出

    回调监控进线程创建和退出 两个注册回调的函数:PsSetCreateProcessNotifyRoutine   进程回调PsSetCreateThreadNotifyRoutine    线程回调分 ...

  5. Linux posix线程库总结

    由于历史原因,2.5.x以前的linux对pthreads没有提供内核级的支持,所以在linux上的pthreads实现只能采用n:1的方式,也称为库实现. 线程的实现,经历了如下发展阶段: Linu ...

  6. Posix线程编程指南(2) 线程私有数据

    概念及作用 在单线程程序中,我们经常要用到"全局变量"以实现多个函数间共享数据.在多线程环境下,由于数据空间是共享的,因此全局变量也为所有线程所共有.但有时应用程序设计中有必要提供 ...

  7. POSIX 线程详解 一种支持内存共享的简捷工具

    线程是有趣的 了解如何正确运用线程是每一个优秀程序员必备的素质.线程类似于进程.如同进程,线程由内核按时间分片进行管理.在单处理器系统中,内核使用时间分片来模拟线程的并发执行,这种方式和进程的相同.而 ...

  8. Posix线程编程指南(3) 线程同步

    互斥锁 尽管在Posix Thread中同样可以使用IPC的信号量机制来实现互斥锁mutex功能,但显然semphore的功能过于强大了,在Posix Thread中定义了另外一套专门用于线程同步的m ...

  9. 通用线程:POSIX 线程详解,第 3 部分 条件互斥量(pthread_cond_t)

    使用条件变量提高效率 本文是 POSIX 线程三部曲系列的最后一部分,Daniel 将详细讨论如何使用条件变量.条件变量是 POSIX 线程结构,可以让您在遇到某些条件时“唤醒”线程.可以将它们看作是 ...

随机推荐

  1. 20165220 实验三 敏捷开发与XP实践 实验报告

    实验三 敏捷开发与XP实践-1 实验要求: 实验三 敏捷开发与XP实践 http://www.cnblogs.com/rocedu/p/4795776.html, Eclipse的内容替换成IDEA ...

  2. 【LeetCode算法-13】Roman to Integer

    LeetCode第13题 Roman numerals are represented by seven different symbols: I, V, X, L, C, D and M. Symb ...

  3. jquery,underscore,lodash那些事儿

    一.参考链接 https://jquery.com/ https://en.wikipedia.org/wiki/JQuery https://developer.mozilla.org/zh-CN/ ...

  4. [SNV]奇怪的错误搜集

    ld: library not found for -XXXXX clang: error: linker command failed with exit code 1 (use -v to see ...

  5. 数据仓库分层ODS DW DM 主题 标签

    数据仓库知识之ODS/DW/DM - xingchaojun的专栏 - CSDN博客 数据仓库为什么要分层 - 晨柳溪 - 博客园 数据仓库的架构与设计 - Trigl的博客 - CSDN博客 数据仓 ...

  6. emitted value instead of an instance of error the scope attribute for scoped slots webpack babel polyfill

    api20180803.vue emitted value instead of an instance of error the scope attribute for scoped slots h ...

  7. SPOJ COT3.Combat on a tree(博弈论 Trie合并)

    题目链接 \(Description\) 给定一棵\(n\)个点的树,每个点是黑色或白色.两个人轮流操作,每次可以选一个白色的点,将它到根节点路径上的所有点染黑.不能操作的人输,求先手是否能赢.如果能 ...

  8. Kotlin基础(三)类、对象和接口

    类.对象和接口 一.定义类的继承结构 一)Kotlin中的接口 Kotlin的接口与Java8中相似,它们可以包含抽象方法的定义以及非抽象方法的实现,但它们不能包含任何状态. interface Cl ...

  9. php 通用数据库类

    <?php // 数据库连接类 class DB{ //私有的属性 private static $dbcon=false; private $host; private $port; priv ...

  10. nodejs 支付宝app支付

    [链接]单笔转账到支付宝账户产品介绍更新时间:https://docs.open.alipay.com/309 const crypto = require('crypto') const momen ...