linux线程的创建与删除
使用linux线程时,编译时需要包含-pthread
选项。
Linux通用API返回0表示成功,返回-1表示失败,并设置errno以标识错误原因。但Pthreads相关的API却与之不同,返回0 表示成功,返回正值表示失败,正值的含义与errno相同。
我们可以使用pthread_create()
来创建线程。创建成功后的线程可以使用如下几种方式终止:
- 线程主函数中执行return语句并返回指定值。
- 调用pthread_exit()函数终止线程
- 调用pthread_cancel()取消线程。
- 任意线程调用了exit(),或者主线程执行了return语句(在main()函数中),都会导致进程中的所有线程立即终止。
当线程终止退出时,分两种情况:
- 线程为可连接(joinable)状态。这也是线程的默认状态。这种状态的线程退出时,必须使用
pthread_join()
函数连接。用于资源回收。否则会产生相当于僵尸进程的线程。除了浪费系统资源以外,僵尸线程若累积过多,应用将再也无法创建新的线程。 - 线程为分离(detached)状态。调用
pthread_detach()
将该线程标记为处于分离(detached)
状态。一旦线程被分离,就不可能再使用pthread_join()来获得它的返回状态,并且该线程也不能再次连接。这种情况一般用于程序员并不关心线程的返回状态,只是希望系统在线程终止时能够自动清理并移除之。
线程使用相关的函数API:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg)
- 创建一个线程
- thread:用于保存线程标识符的实体指针。
- attr:用于指定要创建线程的各种属性。
- start_routine:线程实体函数
- arg:线程实体函数的参数。
- return:
void pthread_exit(void *retval)
- 终止当前线程。
- retval:指定线程的返回内容。pthread_join()可获取返回的内容。retval内容不能是局部变量。必须为全局变量等。
pthread_t pthread_self(void)
- 获取当前线程的id.
int pthread_equal(pthread_t t1, pthread_t t2)
- 检查两个线程是否相等。
- return:if true:返回非0值。if false:返回0.
int pthread_join(pthread_t thread, void **retval)
- 等待某个指定的线程终止。
- thread:指定要等待终止的线程id.
- retval:用于接收线程终止后返回的内容。
- return:if true:0. if false:大于0的错误码。
- 注意:当线程终止时,必须使用pthread_join()函数连接。否则会产生相当于僵尸进程的线程。除了浪费系统资源以外,僵尸线程若累积过多,应用将再也无法创建新的线程。
- 注意:线程之间的关系是对等的。即没有进程中的父子关系等。因此进程中的任意线程均可以调用pthread_join()与该进程的任何其他线程连接起来。
int pthread_detach(pthread_t thread)
- 默认情况下,线程是可连接的(joinable),也就是说,当线程退出时,其他线程可以通过调用pthread_join()获取其返回状态。有时,程序员并不关心线程的返回状态,只是希望系统在线程终止时能够自动清理并移除之。在这种情况下,可以调用pthread_detach()并向thread 参数传入指定线程的标识符,将该线程标记为处于分离(detached)状态。一旦线程被分离,就不可能再使用pthread_join()来获得它的返回状态,并且该线程也不能再次连接。
- thread:线程id
- return:if true:0. if false:大于0的错误码。
如下是一个简单的线程创建例程:
#include <pthread.h>
#include <stdio.h>
#include <error.h>
#include <stdio.h>
#include <stdlib.h>
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
。
int pthread_cancel(pthread_t thread)
- 向目标线程发送取消请求。
- thread:目标线程id
- return:if true:0. if false:大于0的错误码。
int pthread_setcancelstate(int state, int *oldstate)
- 设置当前线程的取消状态。
- state:要设置的状态,如下:
- PTHREAD_CANCEL_DISABLE:线程不可取消。如果此类线程收到取消请求,则会将请求挂起,直至将线程的取消状态置为启用。
- PTHREAD_CANCEL_ENABLE:线程可以取消。新建线程时的默认值。
- oldstate:用于返回之前的状态。
- return:if true:0. if false:大于0的错误码。
int pthread_setcanceltype(int type, int *oldtype)
- 设置当前线程的取消类型。在取消状态设置为PTHREAD_CANCEL_ENABLE时使用。
- type:要设置的类型。
- PTHREAD_CANCEL_ASYNCHRONOUS:异步取消线程。即在线程的任何地方都可以取消。很少用。
- PTHREAD_CANCEL_DEFERED:取消请求保持挂起状态,直至到达
取消点
.新建线程时的默认值。- 什么是
取消点
:简单说就是一些系统规定的可以取消线程的函数。当满足上述条件时(即state为使能,type为挂起),遇到这些函数线程就会终止。系统规定规定的取消点函数很多,如open()
等各种系统调用。详见man手册。
- 什么是
- oldtype:用于返回之前的类型。
- return:if true:0. if false:大于0的错误码。
void pthread_testcancel(void)
- 产生一个取消点。即如果线程中没有使用系统规定取消点函数。可以使用此函数来产生取消点,而不影响程序逻辑。
在线程取消时,可能会涉及到内存释放,互斥量恢复等操作。这时会涉及到线程清理相关的函数。
void pthread_cleanup_push(void (*routine)(void*), void *arg)
- 将指定清理函数压栈。线程因取消请求进入终止状态时,会从栈顶向下依次运行栈中的清理函数。
- routine:清理函数
- arg:清理函数要传入的参数
void pthread_cleanup_pop(int execute)
- 出栈。配合pthread_cleanup_push()使用。它俩必须属于同一语法块,必须一一对应。
- execute:执行状态。
- 0:正常出栈,不执行栈顶的清理函数。
- !0,出栈时,仍然会执行栈顶的清理函数。
关于技术交流
此处后的文字已经和题目内容无关,可以不看。
qq群:825695030
微信公众号:嵌入式的日常
如果上面的文章对你有用,欢迎打赏、点赞、评论。