Linux系统编程——线程:深入解析并发编程的核心机制
目录
线程:CPU运行的最小单位
Linux线程的核心函数
1.线程号获取函数pthread_self
2.线程创建函数pthread_create
3.线程属性设置函数pthread_attr_*
4.线程结束资源回收函数
5.线程退出函数pthread_exit
6.线程取消函数pthread_cancel
7.线程清理注册/弹出函数
8.线程信号发送函数
线程:CPU运行的最小单位
基本概念
在多核处理器成为主流的今天,并发编程已成为提升系统性能的关键技术。在Linux系统中,线程(Thread) 作为轻量级的执行单元,是构建高效并发程序的核心基础。与进程相比,线程共享相同的地址空间,使得数据交换更加高效,上下文切换开销更小,但同时也带来了更复杂的同步和资源管理挑战。本文将全面深入地剖析Linux系统编程中线程的各个方面,为您打开高性能并发编程的大门。进程是系统分配资源的基本单位,线程是操作系统能够进行运算调度的最小单位,是进程中的实际运作单元。
| 对比维度 | 进程 (Process) | 线程 (Thread) | 详细说明 |
|---|---|---|---|
| 定义本质 | 资源分配的基本单位 | CPU调度的基本单位 | 进程是操作系统分配资源(内存、文件等)的独立实体,线程是进程内执行代码的独立序列 |
| 资源占用 | 独立地址空间,资源隔离 | 共享进程地址空间,资源共享 | 进程有自己的虚拟地址空间,线程共享进程的所有资源(除栈、寄存器等私有数据) |
| 创建开销 | 大(复制父进程资源) | 小(复用进程资源) | 进程创建需复制页表、文件描述符表等,线程创建只需分配栈和TCB |
| 销毁开销 | 大(需释放所有资源) | 小(只需清理线程私有数据) | 进程退出需回收整个地址空间,线程退出只回收栈和TCB |
| 上下文切换 | 开销大(需切换地址空间) | 开销小(无需切换地址空间) | 进程切换需刷新TLB、切换页表,线程切换只需切换寄存器、栈指针 |
| 通信方式 | IPC机制(管道、消息队列、共享内存等) | 共享内存(需同步) | 进程间通信需通过内核中介,线程可直接访问共享变量但需同步保护 |
| 通信开销 | 大(需内核介入) | 小(直接内存访问) | 进程通信涉及内核态切换和数据拷贝,线程通信只需内存访问 |
| 隔离性 | 强(独立的地址空间) | 弱(共享地址空间) | 进程崩溃不影响其他进程,线程崩溃可能影响同进程内其他线程 |
| 安全性 | 高(天然隔离) | 低(共享资源) | 进程间不易相互干扰,线程间容易因数据竞争导致问题 |
| 健壮性 | 高(一个进程崩溃不影响系统) | 低(一个线程崩溃可能使整个进程崩溃) | 进程错误相对容易控制,线程错误可能传播到整个进程 |
| 资源共享 | 需显式共享机制 | 自动共享所有进程资源 | 进程间需通过IPC共享数据,线程默认共享全局变量、堆内存等 |
一个进程内可以包含多个线程,这些线程共享进程的资源,但拥有各自独立的执行路径、栈空间和寄存器状态。
进程地址空间
├── 代码段(共享) 所有线程可读
├── 数据段
│ ├── 全局变量(共享) 所有线程共享
│ ├── 堆内存(共享) 需同步访问
│ └── TLS(线程私有) 每个线程独有
├── 栈
│ ├── 主线程栈 每个线程独立
│ ├── 线程1栈
│ └── 线程2栈
└── 文件描述符表(共享) 所有线程共享
生命周期
线程是CPU调度运行的基本单位,包括以下几个运行状态构成的线程生命周期:

Linux线程的核心函数
1.线程号获取函数pthread_self
pthread_t pthread_self(void);
返回当前调用线程的TID,每一个线程都有一个唯一的线程TID,类型为pthread_t,实质为unsigned long int类型。
2.线程创建函数pthread_create
# 创建成功返回0,失败返回-1
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
主线程使用pthread_create函数创建子线程后,子线程会马上被启动运行被传入的函数指针,因此创建子线程前必须准备好子线程符所需要的相关资源。
thread: 传出参数,pthread_t类型,传出线程创建成功后的线程ID写入到这个指针指向的内存中,
attr: 线程的属性, 一般情况下使用默认线程属性即填入NULL,可以创建pthread_attr_t类型变量,通过pthread_attr_*相关函数设置线程属性,然后在创建线程时传入。
# 创建线程属性
pthread_attr_t attr;
# 初始化线程属性变量,此时为默认线程属性,成功返回0,失败返回-1
pthread_attr_init(&attr);
# 通过下列函数可以设置线程属性
# 设置或获取线程属性变量的线程分离状态属性,成功返回0,失败返回-1
# detachstate默认属性为PTHREAD_CREATE_JOINABLE(连接状态),即线程默认属性为父子线程连接的
# 可以设置为PTHREAD_CREATE_DETACHED属性,表示父子线程是分离的
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
# 设置或获取进程内线程的竞争范围
# contentionscope可选值:
* - PTHREAD_SCOPE_SYSTEM: 系统级竞争(可与其他进程的线程竞争CPU)
* - PTHREAD_SCOPE_PROCESS: 进程内竞争(默认,即进程内线程竞争获取的CPU)
int pthread_attr_setscope(pthread_attr_t *attr, int contentionscope);
int pthread_attr_getscope(const pthread_attr_t *attr, int *contentionscope);
# 使用完后必须销毁创建的线程属性变量
pthread_attr_destroy(&attr);
start_routine:void*(*)(void*)类型的函数指针,用于指定子线程完成的工作,否则这个线程无法工作,子线程被创建成功后会立即执行这个函数。由于这个函数类型必须是全局作用域函数即不能是类的成员函数,因此在实现类的封装时无法直接将类成员函数传入,一般通过创建类的静态成员函数并传入类的this指针作为arg参数,然后在该类静态成员函数中通过arg传入的this指针调用执行实际需要执行的类成员函数。
class Thread{
private:
pthread_t tid;
static void* EntryFunction(void* arg){
Thread* thiz = (Thread*)arg;
thiz->RealFunction();
return NULL;
}
void RealFunction(){
# 这里填写子线程真正需要完成的工作
}
public:
Thread(){
int ret = pthread_create(&tid,NULL,Thread::EntryFunction,this);
}
};
arg:void*的万能指针类型,可以指向任何类型的数据,接受任何数据类型的地址,但使用时要转换具体类型。arg作为实参传递到 start_routine 指针指向的函数内部行参,被start_routine函数使用,因此将主线程的数据*arg通过地址方式传递给子线程需要保证:在子线程运行期间,主线程中该数据*arg不会被析构释放,否则可能造成子线程运行访问非法内存。
示例代码
//子线程的回调任务函数callback必须为void * start_routine (void *)
void* callback(void* ptid) //形参接受主线程传入的数据
{
printf("子线程的主线程ptid为:%ld
", *((pthread_t*)ptid)); //子线程执行任务
//使用线程退出函数pthread_exit,退出子线程并且传出子线程数据id的地址&id,注意id不能创建在子线程的栈区数据如局部变量,子线程退出后会被回收
pthread_t* id = (pthread_t*)malloc(sizeof(unsigned long int)); //malloc函数创建在全局区的堆区数据不会被子线程退出回收,采用这种方式传出子线程数据
*id = pthread_self();
pthread_exit(id); //子线程使用线程退出函数pthread_exit退出子线程并传出数据id
return NULL; //线程运行完pthread_exit就结束,不会运行return
}
// 测试线程的基本用法
void ptherad()
{
//使用pthread_self函数获取当前线程的tid
pthread_t ptid = pthread_self();
//主线程使用pthread_create函数创建子线程,回调函数为callback,传入给子线程的数据为&ptid,主线程执行到这里就会分岔,主线程继续向下执行,子线程执行任务函数callback
pthread_t tid1; //作为传出参数,用于保存子线程的tid1
if (pthread_create(&tid1, NULL, callback, &ptid)) //返回非0表示错误,创建子线程失败,tid1作为传出参数保存创建的子线程tid,ptid为主线程传入子线程任务函数的数据
{
perror("pthread_create"); //perror函数报告错误
exit(1); //错误退出程序
}
//子线程回收函数pthread_join,第二个参数为void**类型,因此定义void*类型的指针id,在线程回收函数pthread_join传入id的二级指针&id用于接受pthread_exit传出的数据地址
void* id; //用于接受回收函数保存的子线程数据
//使用线程回收函数pthread_join阻塞主线程,等待子线程运行结束,通过传入指针id地址&id接收线程退出pthread_exit(&t)时传出的数据t地址&t=》*(&id)=&t;接受子线程数据
pthread_join(tid1, &id);
unsigned long int* result_id = (pthread_t*)id; //id为void*类型使用时要转化成具体类型unsigned long int类型
if (*result_id == tid1)
printf("主线程创建的子线程tid为:%ld
", *(result_id));
printf("主线程创建的子线程tid为:%ld
", tid1);
//线程分离函数pthread_detach,分离主线程和子线程,由内核系统回收子线程资源,主线程无需使用pthread_join回收函数回收子线程资源
pthread_detach(tid1);
//主线程可能比子线程先抢到时间片先执行完毕释放虚拟地址空间资源,而子线程和主线程共用同一个虚拟地址空间,导致子线程没有执行直接被释放了。
//sleep(1); //使用sleep函数主线程挂起1s,等待子线程执行完毕,但一般使用pthread_exit函数,使用sleep函数挂起主线程,可能子线程仍没有执行完毕
pthread_exit(NULL); //使用线程退出函数pthread_exit退出,使主线程执行完毕不会释放资源,以保证子线程能继续执行,使用NULL不传出数据。
free(id); //释放id指针指向的堆区数据
}
3.线程属性设置函数pthread_attr_*
# 线程属性变量
pthread_attr_t attr;
设置pthread_attr_t类型的线程属性变量,在pthread_create函数创建线程时的第二个参数传入,用于显示设置创建线程的属性。
# 初始化线程属性变量,此时为默认线程属性,成功返回0,失败返回-1
int pthread_attr_init(pthread_attr_t* pattr);
初始化设置传入的pthread_attr_t类型的线程属性变量,此时为线程的默认属性类型,可以通过下列函数设置线程非默认属性。
父子线程的分离状态属性
# 设置或获取线程属性变量的线程分离状态属性,成功返回0,失败返回-1
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
detachstate默认属性为PTHREAD_CREATE_JOINABLE(连接状态),即线程默认属性为父子线程连接的,可以显式设置为PTHREAD_CREATE_DETACHED属性,表示父子线程是创建分离的。
线程的竞争范围属性
# 成功返回0,失败返回-1
int pthread_attr_setscope(pthread_attr_t *attr, int contentionscope);
int pthread_attr_getscope(const pthread_attr_t *attr, int *contentionscope);
contentionscope可选值的竞争范围:PTHREAD_SCOPE_SYSTEM: 系统级竞争(可与其他进程的线程竞争CPU),PTHREAD_SCOPE_PROCESS: 进程内竞争(线程默认的竞争范围,即进程内线程竞争获取的CPU)。
线程的栈大小属性
# 成功返回0,失败返回-1
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize);
设置或获取线程属性变量的线程栈大小属性。stacksize: 栈大小(字节),默认值由系统定义,通常为8MB(可通过命令ulimit -s查看)。最小值由PTHREAD_STACK_MIN定义。
线程的栈地址属性
# 成功返回0,失败返回-1
int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize);
int pthread_attr_getstack(const pthread_attr_t *attr, void **stackaddr, size_t *stacksize);
设置或获取线程属性变量的线程栈地址和大小属性。成功返回0,失败返回错误码。stackaddr: 自定义栈的起始地址,stacksize: 自定义栈的大小(字节)。一般通过分配指定大小的堆区内存实现。
线程的栈保护大小属性
# 成功返回0,失败返回-1
int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize);
int pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize);
设置或获取线程属性变量的线程栈保护大小属性。guardsize: 栈保护区域大小(字节),默认值为系统页面大小(通常4096字节)。栈保护大小是线程栈末尾(栈底)的一段特殊内存区域,通常设置为不可访问(即保护页)。当线程栈溢出时,线程会访问到保护页,从而触发段错误(SIGSEGV),防止栈溢出破坏其他内存区域。
线程的调度策略继承属性
# 成功返回0,失败返回小于0的错误码
int pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched);
int pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inheritsched);
设置或获取线程属性变量的线程调度策略继承属性。inheritsched: 调度继承策略,可选值:PTHREAD_INHERIT_SCHED (默认值): 继承调度策略,新线程继承创建线程的调度策略和参数;PTHREAD_EXPLICIT_SCHED: 显式调度策略,新线程使用pthread_attr_t类型属性对象中显式设置的调度策略和参数。
线程的调度策略属性
# 成功返回0,失败返回小于0的错误码
int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);
int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy);
设置或获取线程属性变量的线程调度策略属性。
-
policy: 调度策略,可选值:
-
SCHED_OTHER (默认值): 分时调度策略,普通应用程序的标准策略
-
SCHED_FIFO: 先进先出实时调度策略,无时间片,一直运行直到阻塞或更高优先级线程就绪
-
SCHED_RR: 轮转实时调度策略,有时间片轮转,相同优先级线程轮流运行
-
线程的调度参数属性
# 成功返回0,失败返回小于0的错误码。
int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param);
int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param);
设置或获取线程属性变量的线程调度参数属性。param: 调度参数结构体指针,主要包含sched_priority字段,对于SCHED_OTHER: 优先级固定为0,对于SCHED_FIFO和SCHED_RR: 优先级范围为1-99,数字越大优先级越高。
在pthread_create函数使用完后,必须销毁创建的线程属性变量。
# 成功返回0,失败返回-1
int pthread_attr_destroy(pthread_attr_t* pattr);
4.线程结束资源回收函数
线程被创建后,在运行结束需要退出时,需要进行线程资源的回收,常用的线程结束资源回收方式包括:创建线程阻塞回收该线程资源或分离线程由系统回收该线程资源。
如果主线程需要等待子线程结束或获取子线程运行结果,则使用pthread_join阻塞等待子线程运行结束后由主线程回收线程资源;如果子线程是独立的后台任务,且主线程无需等待其结束,则使用pthread_detach分离子线程,由系统内核在子线程运行结束后回收线程资源,也可以在线程创建时设置分离属性(使用pthread_attr_setdetachstate)来创建分离线程。
pthread_join函数阻塞回收线程资源
默认创建的子线程是连接属性的,如果还有子线程在运行,调用pthread_join函数就会阻塞主线程,直到子线程运行结束解除主线程阻塞进行资源的回收,如果不调用pthread_join,且线程是非分离属性的,那么线程资源不会被回收,导致资源泄漏。函数被调用一次只能回收一个子线程,如果有多个子线程则需要循环进行回收。
# 阻塞回收具有连接属性的子线程资源,成功返回0,失败返回小于0的错误号
int pthread_join(pthread_t thread, void **retval);
# 阻塞指定时间回收具有连接属性的子线程资源,指定时间内子线程结束成功返回0,超时返回ETIMEDOUT
int pthread_timedjoin_np(pthread_t thread, void **retval, const struct timespec *abstime);
thread: 要被回收的子线程的线程tid
retval: 传出参数,void**类型二级指针即void* retval万能指针的地址&retval,传入二级指针&retval用于万能指针retval保存pthread_exit(&t) 传递出的数据t地址&t(*(&retval)=&t)或者子线程通过return返回的数据t的地址,因此要求该传出的数据t的地址必须在线程运行结束后还存在,如果不需要回收线程的数据,可以指定为NULL。
abstime:struct timespec结构体类型,表示超时阻塞等待子线程结束回收时间。
#include
struct timespec {
time_t tv_sec; # 秒
long tv_nsec; # 纳秒 (0-999,999,999)
};
pthread_join函数会阻塞等待子线程运行结束回收资源,可以采用pthread_timedjoin_np函数阻塞等待指定时间后的子线程运行,超时直接返回ETIMEDOUT错误,不继续阻塞。
pthread_detach函数分离线程由系统回收线程资源
调用pthread_detach函数会解除线程的父子关系,分离线程后由系统负责进行子线程运行结束后资源的回收,但由于子线程会可能继续使用到主线程资源,因此主线程在运行完前使用pthread_exit退出函数,保证主线程运行退出后不会释放整个进程资源而结束整个进程,避免子线程使用到被释放的进程资源而出错。由于子线程分离后,主线程无法使用pthread_join函数阻塞回收子线程资源,可以主线程调用分离子线程,也可以在子线程中调用主动分离主线程。
# 成功返回0,失败返回小于0的错误号
int pthread_detach(pthread_t thread);
thread: 要被分离的子线程的线程tid
5.线程退出函数pthread_exit
使用pthread_exit函数可以保证当前线程运行结束,并且不会释放所属进程共享资源,可以确保该进程的其他线程可以继续执行,在进程所有线程运行结束后释放进程共享资源,一般用于主线程退出时不立即结束整个进程,确保其他线程可以继续执行。一般情况下使用pthread_exit函数或return进行线程运行结束是等价的,但pthread_exit函数在线程运行结束前会执行pthread_cleanup_push函数注册的资源回收函数,return不会。
# 线程退出函数
void pthread_exit(void *retval);
retval:void*万能指针类型,线程退出的时候携带要传出数据t的地址&t,使当前子线程的主线程使用pthread_join函数会得到该数据t地址&t。如果不需要传出值则指定为NULL即可。
6.线程取消函数pthread_cancel
# 成功返回0,失败返回小于0的错误码
int pthread_cancel(pthread_t thread);
pthread_cancel函数允许一个线程请求取消同一进程中的另一个指定线程号位thread的线程。被取消线程是否取消成功,会包含三个层次的控制:
- 可取消状态 (Cancellation State) - 是否接受取消请求。可以通过以下函数设置线程是否可以取消状态。
# 设置可取消状态 int pthread_setcancelstate(int state, int *oldstate); # 参数state的可选值: #define PTHREAD_CANCEL_ENABLE 1 # 接受取消请求(默认值) #define PTHREAD_CANCEL_DISABLE 0 # 忽略取消请求 -
可取消类型 (Cancellation Type) - 如何响应取消请求。包括延迟取消和异步取消这两种方式:延迟取消会保证被取消线程在取消点进行检查取消请求然后取消线程;异步取消方式会在接收到取消请求后立即取消线程;
# 设置可取消类型 int pthread_setcanceltype(int type, int *oldtype); # 参数type的可选值: #define PTHREAD_CANCEL_DEFERRED 0 # 延迟取消(默认值)- 在取消点检查 #define PTHREAD_CANCEL_ASYNCHRONOUS 1 # 异步取消 - 立即取消 -
取消点 (Cancellation Points) - 何时检查取消请求。常见的取消点包括常见的系统调用函数如:阻塞I/O操作、线程同步函数、睡眠函数、标准I/O等,或者使用pthread_testcancel() 函数实现手动创建取消点。
# 在代码处创建取消点 void pthread_testcancel(void);
在线程在取消点被取消后或在异步取消中被立即取消后,首先会自动按照栈方式执行pthread_cleanup_push函数注册的资源释放函数,然后线程运行结束通过join方式被主线程回收(获取的返回值为PTHREAD_CANCEKED即(void*)-1)或detach方式被系统内核回收线程资源。
7.线程清理注册/弹出函数
为保证线程在运行期间,出现线程取消等情况导致无法在线程结束前释放资源,可以采用pthread_cleanup_push函数和pthread_cleanup_pop函数注册线程清理函数避免资源泄露。
# 注册清理函数
void pthread_cleanup_push(void (*routine)(void*), void* arg);
# 弹出清理函数
void pthread_cleanup_pop(int execute);
routine:注册的资源清理函数的void(*)(void*)类型函数指针。
arg:传入routine函数指针的参数。
execute:是否执行注册的线程清理函数,传入值为1时表示执行,传入值为0时表示不执行。
采用pthread_cleanup_push函数注册的线程清理函数会在以下几种情况下时按照LIFO注册顺序方式被依次执行:①线程被pthread_cancel函数取消时;②调用pthread_exit函数时;③显式调用pthread_cleanup_pop(1)时。线程正常通过return返回时不执行。
在线程正常通过return或pthread_exit函数返回,并且由于保证可以正常编译pthread_cleanup_push函数和pthread_cleanup_pop函数必须成对出现,因此在return正常结束前,必须使用pthread_cleanup_pop函数弹出相应数量的pthread_cleanup_push函数注册的线程清理函数传入1表示执行或0表示不执行。
示例代码
#include
#include
#include
#include
// 清理函数1
void cleanup_file(void* arg) {
FILE** file_ptr = (FILE**)arg;
if (file_ptr && *file_ptr) {
printf("清理: 关闭文件 (fd: %p)
", (void*)*file_ptr);
fclose(*file_ptr);
*file_ptr = NULL;
}
}
// 清理函数2
void cleanup_memory(void* arg) {
char** mem_ptr = (char**)arg;
if (mem_ptr && *mem_ptr) {
printf("清理: 释放内存 (地址: %p, 大小: 1024)
", (void*)*mem_ptr);
free(*mem_ptr);
*mem_ptr = NULL;
}
}
// 清理函数3
void cleanup_mutex(void* arg) {
pthread_mutex_t** mutex_ptr = (pthread_mutex_t**)arg;
if (mutex_ptr && *mutex_ptr) {
printf("清理: 解锁互斥锁 (地址: %p)
", (void*)*mutex_ptr);
pthread_mutex_unlock(*mutex_ptr);
}
}
// 可取消的线程函数
void* cancellable_worker(void* arg) {
int thread_id = *(int*)arg;
printf("线程 %d: 启动,注册清理函数
", thread_id);
// 分配线程本地资源
FILE* log_file = fopen("thread_log.txt", "w");
char* buffer = malloc(1024);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
if (!log_file || !buffer) {
printf("线程 %d: 资源分配失败
", thread_id);
return NULL;
}
pthread_mutex_lock(&mutex);
// 注册清理函数(必须成对出现,后进先出)
pthread_cleanup_push(cleanup_mutex, &mutex); // 第3个注册
pthread_cleanup_push(cleanup_memory, &buffer); // 第2个注册
pthread_cleanup_push(cleanup_file, &log_file); // 第1个注册
printf("线程 %d: 已注册3个清理函数,顺序为:文件→内存→互斥锁
", thread_id);
// 启用取消
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
// 模拟工作
for (int i = 0; i < 5; i++) {
fprintf(log_file, "线程 %d: 日志条目 %d
", thread_id, i);
fflush(log_file);
sprintf(buffer, "迭代 %d 的数据", i);
printf("线程 %d: %s
", thread_id, buffer);
sleep(1);
// 创建取消点
pthread_testcancel();
}
printf("线程 %d: 工作完成,正常退出
", thread_id);
// 正常退出,不执行清理函数
pthread_cleanup_pop(0); // 弹出文件清理,不执行
pthread_cleanup_pop(0); // 弹出内存清理,不执行
pthread_cleanup_pop(0); // 弹出互斥锁清理,不执行
// 手动释放资源
printf("线程 %d: 手动释放资源
", thread_id);
fclose(log_file);
free(buffer);
pthread_mutex_unlock(&mutex);
pthread_mutex_destroy(&mutex);
return (void*)(long)thread_id;
}
8.线程信号发送函数
可以给指定线程发送信号,在线程绑定运行中通过信号注册函数sigaction绑定指定信号和处理函数时,给该线程发送绑定的信号会执行相应的绑定的函数处理。
# 成功返回0,失败返回小于0的错误码
int pthread_kill(pthread_t thread, int sig);
thread:指定的线程号
sig:发送的信号包括:SIGUSR1/SIGUSR2(用户自定义信号,用于绑定特定的处理函数实现指定的功能)。








