目录
一、线程的创建与回收
二、线程的分离
三、线程的取消与清理
#include
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*routine)(void *), void *arg);
成功返回0,失败时返回错误码
thread 线程对象
attr 线程属性,NULL代表默认属性
routine 线程执行的函数
arg 传递给routine的参数 ,参数是void * ,注意传递参数格式,
传值:
ret = pthread_create(&pthread[i],NULL,(void*)testattr,(void*)i);
printf("arg = %d\n",(int)arg);
#include
#include
#include
#include
void* testattr(void* arg)
{
printf("child pthread\n");
printf("arg = %d\n",(int)arg);
// printf("arg = %d\n",*(int*)arg);
return NULL;
}
int main(int argc,const char* argv[])
{
pthread_t pthread[5];
int ret = 0;
int i = 0;
for(i = 0;i<5;i++)
{
ret = pthread_create(&pthread[i],NULL,(void*)testattr,(void*)i);
// ret = pthread_create(&pthread[i],NULL,(void*)testattr,(void*)&i);
printf("create\n");
// sleep(1);
if(ret == EOF)
{
perror("pthread_create");
exit(-1);
}
}
printf("test\n");
for(i = 0; i<5;i++)
{
printf("wait\n");
pthread_join(pthread[i],NULL);
}
return 0;
}
打印结果:
create
create
create
create
create
test
wait
child pthread
arg = 4
child pthread
arg = 3
child pthread
arg = 2
child pthread
arg = 1
child pthread
arg = 0
wait
wait
wait
wait
传址:加sleep(1)
linux@linux:~/code_Linux/code_2024_1_24/Linux_csdn$ vim pthread.c
linux@linux:~/code_Linux/code_2024_1_24/Linux_csdn$ gcc -o pthread pthread.c -lpthread
linux@linux:~/code_Linux/code_2024_1_24/Linux_csdn$ ./pthread
create
child pthread
arg = 0
create
child pthread
arg = 1
create
child pthread
arg = 2
create
child pthread
arg = 3
create
child pthread
arg = 4
test
wait
wait
wait
wait
wait
传址:不加sleep(1)
linux@linux:~/code_Linux/code_2024_1_24/Linux_csdn$ gcc -o pthread pthread.c -lpthread
linux@linux:~/code_Linux/code_2024_1_24/Linux_csdn$ ./pthread
create
create
create
create
create
test
wait
child pthread
arg = 0
child pthread
arg = 0
child pthread
arg = 0
child pthread
arg = 0
child pthread
arg = 0
wait
wait
wait
wait
#include
#include
#include
#include
void* testattr(void* arg)
{
printf("child pthread\n");
// printf("arg = %d\n",(int)arg);
printf("arg = %d\n",*(int*)arg);
return NULL;
}
int main(int argc,const char* argv[])
{
pthread_t pthread[5];
int ret = 0;
int i = 0;
for(i = 0;i<5;i++)
{
// ret = pthread_create(&pthread[i],NULL,(void*)testattr,(void*)i);
ret = pthread_create(&pthread[i],NULL,(void*)testattr,(void*)&i);
printf("create\n");
sleep(1);
if(ret == EOF)
{
perror("pthread_create");
exit(-1);
}
}
printf("test\n");
for(i = 0; i<5;i++)
{
printf("wait\n");
pthread_join(pthread[i],NULL);
}
return 0;
}
当使用 (void*)i 作为参数传递给 pthread_create 时
传递的是 i 的值的拷贝,而不是 i 的地址。
因此,每个线程得到的参数都是独立的,不会在后续循环迭代中受到影响。
而当用 (void*)&i 作为参数传递时,你传递的是 i 的地址,所有线程共享相同的地址。
因此,在后续循环迭代中,i 的值可能会改变,影响所有线程的参数。
当你使用 (void*)&i 并添加 sleep(1) 时,主线程被强制等待一秒钟,
这可能给其他线程足够的时间启动并复制当前的 i 值。
但这并不是一个稳定的解决方案,因为线程调度的行为可能会因系统和其他因素而异。
因此,使用 (void*)i 避免这个问题,因为每个线程都得到了 i 的独立拷贝,而不受主线程循环迭代的影响。
pthread_join 是一个用于等待指定线程结束的函数。
通过使用 pthread_join,主线程等待每个创建的子线程执行完毕,然后再继续执行。
上述代码,主线程会调用 pthread_join(pthread[i], NULL); 等待每个线程结束。
这样做的目的是确保主线程在退出之前等待每个子线程的完成。
这是因为主线程和子线程是并行执行的,如果主线程提前退出,那么所有尚未完成的子线程可能会被强制终止,导致未完全执行的线程。
pthread_join 提供了一种等待子线程完成的机制,确保主线程在所有子线程完成后再退出。
线程的回收:
使用pthread_join 函数:
#include
int pthread_join(pthread_t thread, void **retval);
注意:pthread_join 是阻塞函数,如果回收的线程没有结束,则一直等待
linux@linux:~/code_Linux/code_2024_1_24/Linux_csdn$ ./pthread_joinpthread = 3075496768
arg = 5
retval = hello,world
linux@linux:~/code_Linux/code_2024_1_24/Linux_csdn$ cat pthread_join.c
#include
#include
#include
#include
void* testattr(void* arg)
{
printf("pthread = %lu\n",(unsigned long)pthread_self());
printf("arg = %d\n",*(int*)arg);
pthread_exit("hello,world");
}
int main(int argc,const char* argv[])
{
pthread_t pthread;
int arg = 5;
pthread_create(&pthread,NULL,(void*)testattr,&arg);
void* retval;
pthread_join(pthread,&retval);
printf("retval = %s\n",(char*)retval);
exit(0);
}
两种方式:
1 使用pthread_detach
linux@linux:~/code_Linux/code_2024_1_24/Linux_csdn$ ./pthread_detach
This is child pthread
This is child pthread
This is child pthread
This is child pthread
This is child pthread
^C
linux@linux:~/code_Linux/code_2024_1_24/Linux_csdn$ cat pthread_detach.c#include
#include
#include
#include
void* testattr(void* arg)
{
pthread_detach(pthread_self());
printf("This is child pthread\n");
sleep(5);
pthread_exit("hello,world");
}
int main(int argc,const char* argv[])
{
pthread_t pthread;
int i = 0;
for(i = 0;i<5;i++)
{
pthread_create(&pthread,NULL,testattr,NULL);
}
while(1)
{
sleep(1);
}
return 0;
}
2 创建线程时候设置为分离属性
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
线程的取消:
意义:随时杀掉一个线程
int pthread_cancel(pthread_t thread);
注意:线程的取消要有取消点才可以,不是说取消就取消,线程的取消点主要是阻塞的系统调用
如果没有取消点,手动设置一个
void pthread_testcancel(void);
设置取消使能或禁止
int pthread_setcancelstate(int state, int *oldstate);
PTHREAD_CANCEL_ENABLE
PTHREAD_CANCEL_DISABLE
设置取消类型
int pthread_setcanceltype(int type, int *oldtype);
PTHREAD_CANCEL_DEFERRED 等到取消点才取消
PTHREAD_CANCEL_ASYNCHRONOUS 目标线程会立即取消
#include
#include
#include
void *func(void *arg){
printf("This is child thread\n");
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
// while(1)
{
sleep(5);
pthread_testcancel();
}
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
while(1){
sleep(1);
}
pthread_exit("thread return");
}
int main(){
pthread_t tid;
void *retv;
int i;
pthread_create(&tid,NULL,func,NULL);
sleep(1);
pthread_cancel(tid);
pthread_join(tid,&retv);
// printf("thread ret=%s\n",(char*)retv);
while(1){
sleep(1);
}
}
pthread_testcancel 的作用是检查是否有取消请求在当前线程中排队等待执行。
取消请求通常是通过调用 pthread_cancel 函数发起的,用于请求取消线程的执行。
在多线程环境中,可以使用 pthread_testcancel 来主动检查是否有取消请求,并在适当的地方进行线程取消。
这对于长时间运行的线程中插入取消点(cancellation points)是有用的,以确保线程可以在需要时及时取消。
示例代码:
#include
#include
void* myThreadFunction(void* arg) {
for (int i = 0; i < 10; ++i) {
// Do some work
printf("Working...\n");
// Check for cancellation request
pthread_testcancel();
// More work
}
return NULL;
}
int main() {
pthread_t myThread;
pthread_create(&myThread, NULL, myThreadFunction, NULL);
// Allow some time for the thread to execute
sleep(2);
// Cancel the thread
pthread_cancel(myThread);
// Wait for the thread to finish
pthread_join(myThread, NULL);
printf("Main thread finished.\n");
return 0;
}
在上面的例子中,pthread_testcancel
在循环中被调用,以检查是否有取消请求。如果有取消请求,线程会在这个点被取消。在主线程中,pthread_cancel
被用于请求取消 myThread
线程的执行。请注意,使用线程取消时需要小心,确保在适当的地方插入取消点,以避免资源泄漏或不一致的状态。
线程的清理:
必要性: 当线程非正常终止,需要清理一些资源。
void pthread_cleanup_push(void (*routine) (void *), void *arg)
void pthread_cleanup_pop(int execute)
pthread_cleanup_push
函数:
pthread_cleanup_push
来指定在线程取消(被终止)时需要执行的清理处理函数。void pthread_cleanup_push(void (*routine)(void*), void *arg);
pthread_cleanup_pop
函数:
pthread_cleanup_push
配套使用,指定了清理处理函数后,需要使用 pthread_cleanup_pop
来相应地移除它。void pthread_cleanup_pop(int execute);
execute
参数:如果 execute
的值为非零,则执行栈顶的清理处理例程,如果为零,则不执行。这样,你可以在取消点选择是否执行清理处理。示例代码:
#include
#include
void cleanup_handler(void *arg) {
printf("Cleanup: %s\n", (const char*)arg);
}
void* myThreadFunction(void* arg) {
// Push cleanup handler onto the cleanup stack
pthread_cleanup_push(cleanup_handler, "Cleanup routine 1");
printf("Thread is running...\n");
// Simulate some work
for (int i = 0; i < 3; ++i) {
sleep(1);
printf("Working...\n");
}
// Pop the cleanup handler (execute it)
pthread_cleanup_pop(1);
return NULL;
}
int main() {
pthread_t myThread;
pthread_create(&myThread, NULL, myThreadFunction, NULL);
// Allow some time for the thread to execute
sleep(2);
// Cancel the thread
pthread_cancel(myThread);
// Wait for the thread to finish
pthread_join(myThread, NULL);
printf("Main thread finished.\n");
return 0;
}
在上述例子中,pthread_cleanup_push
用于将 cleanup_handler
函数压入清理处理栈,然后在线程的执行过程中,pthread_cleanup_pop
用于弹出并执行清理处理。这样,在线程被取消时,cleanup_handler
将被执行,实现了清理处理的功能。
routine 函数被执行的条件:
1. 被pthread_cancel取消掉。
#include
#include
#include
#include
void cleanup(void* argc)
{
printf("cleanup,argc = %s\n",(char*)argc);
}
void* testattr(void* argc)
{
printf("child pthread\n");
// pthread_setcancelstate(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
pthread_cleanup_push(cleanup,"abcd");
while(1)
{
sleep(1);//创造取消点
}
//pthread_testcancel();
pthread_cleanup_pop(1);//正数进行释放
pthread_exit("hello,world");
}
int main(int argc,const char* argv[])
{
pthread_t pthread;
pthread_create(&pthread,NULL,testattr,NULL);
void* retv;
sleep(1);//足够的时间去创建线程
pthread_cancel(pthread);//杀死线程
pthread_join(pthread,&retv);//回收线程
//printf("%s\n",(char*)retv);
while(1)
{
sleep(1);
}
exit(0);
}
pthread_cancel
不会立即终止线程,而是在等待线程到达取消点时终止。在testattr
函数中,sleep(1)
提供了一个取消点(cancellation point),使得线程在这里可以被取消。 pthread_cancel
函数用于请求取消线程的执行,但线程只有在达到取消点时才会被实际取消。取消点是线程可以被取消的一些特定位置,确保在这些位置上线程的状态是一致的。
sleep 函数是标准的取消点,它允许线程进入睡眠状态并等待一段时间。
当线程在 sleep 中时,它是可以被取消的。
如果你去掉 sleep(1),那么 while(1) 循环中就不再有明显的取消点。
取消点的存在是为了确保在某些位置上线程的状态是一致的,并且允许取消操作发生。
如果你的循环中没有明显的取消点,那么线程在执行过程中可能会在任意位置被取消,这可能导致一些不一致的状态。
在没有取消点的情况下,线程可能会在不确定的位置被取消,可能导致资源泄漏或其他问题。
因此,为了确保线程在合适的时候被取消,通常会在代码中插入明确的取消点。
这就是为什么在上述代码中使用了 sleep(1) 来创建一个取消点,以确保在 sleep 的时候线程是可以被取消的。
一些标准的库函数,例如 sleep、pthread_join、pthread_testcancel,都是取消点。
这意味着当线程执行这些函数时,系统会检查是否有取消请求。
2. 执行pthread_exit
linux@linux:~/code_Linux$ ./test
child pthread
cleanup,argc = abcd
^C
#include
#include
#include
#include
void cleanup(void* argc)
{
printf("cleanup,argc = %s\n",(char*)argc);
}
void* testattr(void* argc)
{
printf("child pthread\n");
pthread_cleanup_push(cleanup,"abcd");
pthread_exit("thread pop\n");
pthread_cleanup_pop(0);
}
int main(int argc,const char* argv[])
{
pthread_t pthread;
pthread_create(&pthread,NULL,testattr,NULL);
void* retv;
sleep(1);//足够的时间去创建线程
pthread_join(pthread,&retv);//回收线程
while(1)
{
sleep(1);
}
exit(0);
}
现在我们将exit放在后面去:
pthread_cleanup_pop(0);
pthread_exit("thread pop\n");
运行结果为:
从上可以看出child pthread虽然成功退出了,但是
void cleanup(void* argc)
{
printf("cleanup,argc = %s\n",(char*)argc);
}
并没有正常执行,因为没有遇到pthread_exit();只需要进行修改,把pop的参数修改为非0即可打印出我们想要的结果,如下:
3. 非0参数执行pthread_cleanup_pop()
注意:
1. 必须成对使用,即使pthread_cleanup_pop不会被执行到也必须写上,否则编译错误。
2.pthread_cleanup_pop()被执行且参数为0,pthread_cleanup_push回调函数routine不会被执行。
3.thread_cleanup_push 和pthread_cleanup_pop可以写多对,routine执行顺序正好相反。
linux@linux:~/code_Linux$ vim test.c
linux@linux:~/code_Linux$ gcc -o test test.c -lpthread
linux@linux:~/code_Linux$ ./test
child pthread
cleanup,argc = haha
cleanup,argc = abcd
^C
先打印haha,再打印的abcd
#include
#include
#include
#include
void cleanup(void* argc)
{
printf("cleanup,argc = %s\n",(char*)argc);
}
void cleanup1(void* argc)
{
printf("cleanup,argc = %s\n",(char*)argc);
}
void* testattr(void* argc)
{
printf("child pthread\n");
// pthread_setcancelstate(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
pthread_cleanup_push(cleanup,"abcd");
pthread_cleanup_push(cleanup1,"haha");
// while(1)
// {
sleep(1);//创造取消
//pthread_testcancel();
// pthread_exit("thread pop\n");
pthread_cleanup_pop(1);
pthread_cleanup_pop(1);
pthread_exit("hello,world");
}
int main(int argc,const char* argv[])
{
pthread_t pthread;
pthread_create(&pthread,NULL,testattr,NULL);
void* retv;
sleep(1);//足够的时间去创建线程
// pthread_cancel(pthread);//杀死线程
pthread_join(pthread,&retv);//回收线程
//printf("%s\n",(char*)retv);
while(1)
{
sleep(1);
}
exit(0);
}
4. 线程内的return 可以结束线程,也可以给pthread_join返回值,但不能触发pthread_cleanup_push里面的回调函数,所以我们结束线程尽量使用pthread_exit退出线程。
void* testattr(void* argc)
{
printf("child pthread\n");
pthread_cleanup_push(cleanup,"abcd");
pthread_cleanup_push(cleanup1,"haha");
pthread_cancel(pthread_self());
printf("test pthread_self\n");
pthread_cleanup_pop(1);
pthread_cleanup_pop(1);
pthread_exit("hello,world");
}
分明已经取消了,为什么还会打印test,是因为没有取消点。
linux@linux:~/code_Linux$ gcc -o test test.c -lpthread
linux@linux:~/code_Linux$ ./test
child pthread
test pthread_self
cleanup,argc = haha
cleanup,argc = abcd
加入后打印没有变化,是因为默认是在取消点处取消
可以设置立即取消。 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
linux@linux:~/code_Linux$ gcc -o test test.c -lpthread
linux@linux:~/code_Linux$ ./test
child pthread
cleanup,argc = haha
cleanup,argc = abcd
^C
linux@linux:~/code_Linux$ cat test.c
#include
#include
#include
#include
void cleanup(void* argc)
{
printf("cleanup,argc = %s\n",(char*)argc);
}
void cleanup1(void* argc)
{
printf("cleanup,argc = %s\n",(char*)argc);
}
void* testattr(void* argc)
{
printf("child pthread\n");
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
pthread_cleanup_push(cleanup,"abcd");
pthread_cleanup_push(cleanup1,"haha");
pthread_cancel(pthread_self());
printf("test pthread_self\n");
while(1)
{
printf("sleep\n");
sleep(1);//创造取消
}
//pthread_testcancel();
// pthread_exit("thread pop\n");
pthread_cleanup_pop(1);
pthread_cleanup_pop(1);
pthread_exit("hello,world");
}
int main(int argc,const char* argv[])
{
pthread_t pthread;
pthread_create(&pthread,NULL,testattr,NULL);
void* retv;
sleep(1);//足够的时间去创建线程
// pthread_cancel(pthread);//杀死线程
pthread_join(pthread,&retv);//回收线程
//printf("%s\n",(char*)retv);
while(1)
{
sleep(1);
}
exit(0);
}
最后注意:return线程内的return 可以结束线程,也可以给pthread_join返回值,但不能触发
#include
#include
#include
void cleanup(void *arg)
{
printf("cleanup,arg=%s\n",(char*)arg);
}
void cleanup2(void* arg)
{
printf("cleanup2,arg=%s\n",(char*)arg);
}
void *func(void *arg)
{
printf("This is child thread\n");
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
pthread_cleanup_push(cleanup,"abcd");
pthread_cleanup_push(cleanup2,"efgh");
{
sleep(1);
}
return "1234";
while(1)
{
printf("sleep\n");
sleep(1);
}
pthread_exit("thread return");
pthread_cleanup_pop(1);
pthread_cleanup_pop(1);
sleep(10);
pthread_exit("thread return");
}
int main()
{
pthread_t tid;
void *retv;
int i;
pthread_create(&tid,NULL,func,NULL);
sleep(1);
pthread_join(tid,&retv);
printf("thread ret=%s\n",(char*)retv);
while(1)
{
sleep(1);
}
}