5. 线程分离
在某些情况下,程序中的主线程有属于自己的业务处理流程,如果让主线程负责子线程的资源回收,调用 pthread_join() 只要子线程不退出主线程就会一直被阻塞,主要线程的任务也就不能被执行了。
在线程库函数中为我们提供了线程分离函数 pthread_detach(),调用这个函数之后指定的子线程就可以和主线程分离,当子线程退出的时候,其占用的内核资源就被系统的其他进程接管并回收了。线程分离之后在主线程中使用 pthread_join() 就回收不到子线程资源了。
#include <pthread.h>
// 参数就子线程的线程ID, 主线程就可以和这个子线程分离了
int pthread_detach(pthread_t thread);
在主线程中创建子线程,并调用线程分离函数,实现了主线程和子线程的分离:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
struct Test
{
/* data */
int num;
int age;
};
void* callback(void* arg)
{
for(int i=0; i<5; i++)
{
printf("子线程: i= %d\n",i);
}
printf("子线程:%ld\n",pthread_self());
//struct Test t; //定义在此的话,子线程退出将会在栈中释放 t,导致无法访问,可定义为全局变量,
//或者在主线程栈中创建,传给子线程
struct Test* t = (struct Test*) arg;
t->num = 100;
t->age = 18;
pthread_exit(t);
return NULL;
}
int main()
{
struct Test t;
pthread_t tid;
pthread_create(&tid,NULL,callback,&t);//使用主线程栈,将t传给子线程
printf("主线程:%ld\n",pthread_self());
pthread_detach(tid);
pthread_exit(NULL);
return 0;
}
6. 其他线程函数
6.1 线程取消
线程取消的意思就是在某些特定情况下在一个线程中杀死另一个线程。使用这个函数杀死一个线程需要分两步:
1.在线程 A 中调用线程取消函数 pthread_cancel,指定杀死线程 B,这时候线程 B 是死不了的
2.还要在线程 B 中进程一次系统调用(从用户区切换到内核区),否则线程 B 可以一直运行。
#include <pthread.h>
// 参数是子线程的线程ID
int pthread_cancel(pthread_t thread);
参数:要杀死的线程的线程 ID
返回值:函数调用成功返回 0,调用失败返回非 0 错误号。
在下面的示例代码中,主线程调用线程取消函数,只要在子线程中进行了系统调用,当子线程执行到这个位置就挂掉了。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
// 子线程的处理代码
void* working(void* arg)
{
int j=0;
for(int i=0; i<9; ++i)
{
j++;
}
// 这个函数会调用系统函数, 因此这是个间接的系统调用
printf("我是子线程, 线程ID: %ld\n", pthread_self());
for(int i=0; i<9; ++i)
{
printf(" child i: %d\n", i);
}
return NULL;
}
int main()
{
// 1. 创建一个子线程
pthread_t tid;
pthread_create(&tid, NULL, working, NULL);
printf("子线程创建成功, 线程ID: %ld\n", tid);
// 2. 子线程不会执行下边的代码, 主线程执行
printf("我是主线程, 线程ID: %ld\n", pthread_self());
for(int i=0; i<3; ++i)
{
printf("i = %d\n", i);
}
// 杀死子线程, 如果子线程中做系统调用, 子线程就结束了
pthread_cancel(tid);
// 让主线程自己退出即可
pthread_exit(NULL);
return 0;
}
Note:
关于系统调用有两种方式:
- 直接调用 Linux 系统函数
- 调用标准 C 库函数,为了实现某些功能,在 Linux 平台下标准 C 库函数会调用相关的系统函数
6.2 线程 ID 比较
在 Linux 中线程 ID 本质就是一个无符号长整形,因此可以直接使用比较操作符比较两个线程的 ID,但是线程库是可以跨平台使用的,在某些平台上 pthread_t 可能不是一个单纯的整形,这中情况下比较两个线程的 ID 必须要使用比较函数,函数原型如下:
#include <pthread.h>
int pthread_equal(pthread_t t1, pthread_t t2);
参数:t1 和 t2 是要比较的线程的线程 ID
返回值:如果两个线程 ID 相等返回非 0 值,如果不相等返回 0