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() 和 /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) 中列出的函数就是或者是取消点。
当一个取消请求起作用时,下面的步骤会按顺序发生。
- 取消清理函数会被出栈并被执行。
- 线程相关数据会被析构,顺序不确定。
- 线程终止。
以上的步骤会异步的执行,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 线程的创建与退出的更多相关文章
- Linux多线程编程——线程的创建与退出
POSIX线程标准:该标准定义了创建和操纵线程的一整套API.在类Unix操作系统(Unix.Linux.Mac OS X等)中,都使用Pthreads作为操作系统的线程.Windows操作系统也有其 ...
- LINUX多线程(一)(创建和退出)
1. Linux多线程概述 1.1. 概述 进程是系统中程序执行和资源分配的基本单位.每个进程有自己的数据段.代码段和堆栈段.这就造成进程在进行切换等操作时都需要有比较负责的上下文切换等动作.为了进一 ...
- Posix线程编程指南(1) 线程创建与取消
线程创建 1.1 线程与进程 相对进程而言,线程是一个更加接近于执行体的概念,它可以与同进程中的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列.在串行程序基础上引入线程和进程是为了提高程序的 ...
- Win64 驱动内核编程-12.回调监控进线程创建和退出
回调监控进线程创建和退出 两个注册回调的函数:PsSetCreateProcessNotifyRoutine 进程回调PsSetCreateThreadNotifyRoutine 线程回调分 ...
- Linux posix线程库总结
由于历史原因,2.5.x以前的linux对pthreads没有提供内核级的支持,所以在linux上的pthreads实现只能采用n:1的方式,也称为库实现. 线程的实现,经历了如下发展阶段: Linu ...
- Posix线程编程指南(2) 线程私有数据
概念及作用 在单线程程序中,我们经常要用到"全局变量"以实现多个函数间共享数据.在多线程环境下,由于数据空间是共享的,因此全局变量也为所有线程所共有.但有时应用程序设计中有必要提供 ...
- POSIX 线程详解 一种支持内存共享的简捷工具
线程是有趣的 了解如何正确运用线程是每一个优秀程序员必备的素质.线程类似于进程.如同进程,线程由内核按时间分片进行管理.在单处理器系统中,内核使用时间分片来模拟线程的并发执行,这种方式和进程的相同.而 ...
- Posix线程编程指南(3) 线程同步
互斥锁 尽管在Posix Thread中同样可以使用IPC的信号量机制来实现互斥锁mutex功能,但显然semphore的功能过于强大了,在Posix Thread中定义了另外一套专门用于线程同步的m ...
- 通用线程:POSIX 线程详解,第 3 部分 条件互斥量(pthread_cond_t)
使用条件变量提高效率 本文是 POSIX 线程三部曲系列的最后一部分,Daniel 将详细讨论如何使用条件变量.条件变量是 POSIX 线程结构,可以让您在遇到某些条件时“唤醒”线程.可以将它们看作是 ...
随机推荐
- GYM 101755 K.Video Reviews 【贪心】+【二分】
<题目链接> 题目大意: 一家公司想让n个人给他们的产品评论,所以依次去找这n个人,第i个人会评论当且仅当已经有ai个人评论或他确实对这个产品感兴趣,但是这n个人都不对这个产品感兴趣,问这 ...
- springboot2 config_toolkit 并且设置全局获取数据GlobalUtil
本文只贴相关代码段,完整代码请移步至本人github,若是喜欢,可以star给予支持 作者:cnJun 博客专栏: https://www.cnblogs.com/cnJun/ 本文实现目标 重要的配 ...
- TensorFlow下利用MNIST训练模型并识别自己手写的数字
最近一直在学习李宏毅老师的机器学习视频教程,学到和神经网络那一块知识的时候,我觉得单纯的学习理论知识过于枯燥,就想着自己动手实现一些简单的Demo,毕竟实践是检验真理的唯一标准!!!但是网上很多的与t ...
- 学生成绩管理系统C++
今天晚上终于做完了学生成绩管理系统!激动!开心!!!哈哈哈~~~~ 总共298行代码,第一次写这么多. 其中遇到了好多困难,也烦恼了好久,不过最终都解决了! 做了之后果然,满满的成就感!抑制不住的兴奋 ...
- 如何删除pagefile.sys
经常使用电脑的用户就会发现系统自带了虚拟内存文件pagefile.sys,若是电脑出现内存不足情况,其就会调用虚拟内存来执行程序,以防止系统内存崩溃.不过,虚拟内存没有真实的内存读取速度快,而且会占用 ...
- SQL 分隔字符串
ALTER FUNCTION dbo.fn_Split ( ), ) ) RETURNS @table_Value TABLE ( SortNo ,) NOT NULL, Value ) COLLAT ...
- HDU.4700.Flow(构造 最小割树)
题目链接 \(Description\) 给定\(n\)以及\(n\)个点任意两点之间的最大流,求一张无向图满足给定条件. \(n\leq100\). \(Solution\) 有些类似最小割树. 我 ...
- 3ds max学习笔记-- 灯光调节
8,软件内默认有两盏灯,在渲染时不产生投影和高光点.手动添加灯光时,系统灯光自动关闭: 1,标准灯光:聚光灯,平行光,泛光 ,天光: a,聚光灯:目标聚光灯和自由聚光灯,当目标聚光灯的[目标]取消勾选 ...
- 使用iscroll,无法正常滑动的原因
iscroll的dom元素的结构是固定的,swiper是容器,scroll是需要滚动的容器,list是滚动的内容 <div class="swiper"> <di ...
- 多重背包--java
多重背包 有N种物品和一个容量为V的背包.第i种物品最多有n[i]件可用,每件费用是c[i],价值 是w[i].求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大母函数的思想也 ...