线程基本API
线程的概念
进程是分配资源的最小单位,所有线程都共享一套内存,状态是独立的
基本接口
1.线程的创建
函数原型
int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void *(*start_routine)(void *),void *arg
);
参数解释
thread:新线程的TID
attr:线程属性,若创建标准线程则该参数可设置为NULL
start_routine:线程函数
arg:线程函数的参数
示例
// simpleThread.c
#include <pthread.h>void *doSomething(void *arg)//arg为返回值
{// ...
}int main()
{// 创建一条线程,并让其执行函数 doSomething()pthread_t tid;pthread_create(&tid, NULL, doSomething, NULL);// ...
}
线程的各种接口单独放在线程库中,因此在编译带线程的代码时,必须要指定链接线程库phread,如下:
gec@ubuntu:~$ gcc simpleThread.c -o simpleThread -lpthread
注意
由于线程函数的并发性,在线程中访问共享资源需要特别小心,因为这些共享资源会被多个线程争抢,形成“竞态”。
2.线程的退出
函数原型
#include <pthread.h>void pthread_exit(void *retval);
注意此函数与exit的区别:
pthread_exit(): 退出当前线程
exit(): 退出当前进程(即退出进程中的所有线程)
主线程退出后,其余线程可以继续运行
3.线程的接合
与进程类似,线程退出之后不会立即释放其所占有的系统资源,而会成为一个僵尸线程,可使用 pthread_join() 来释放僵尸线程的资源,并可获得其退出时返回的退出值,该接口函数被称为线程的接合函数:
函数原型
#include <pthread.h>int pthread_join(pthread_t tid, void **val);
参数说明
pthread_t tid:线程的ID
val:线程的返回值 注意:线程返回值是一个指针,其中指向的内容必须没有被自动释放,例如局部变量
函数说明
若指定tid的线程尚未退出,那么该函数将持续阻塞。
若只想阻塞等待指定线程tid退出,而不想要其退出值,那么val可置为NULL。
若指定tid的线程处于分离状态,或不存在,则该函数会出错返回。
4.其他API
获取线程TID
#include <pthread.h>pthread_t pthread_self(void);
TID仅限于进程内部的线程间有效
线程的分离与联合
注意
分离时机:pthread_detach 可以在线程创建后立即调用,也可以在线程运行期间调用。但通常建议在线程创建后尽快分离。
线程终止:分离线程的终止状态无法被其他线程获取,因此如果需要线程的返回值,不能使用分离。
资源泄漏:如果线程既未被分离,也未被 pthread_join,可能会导致资源泄漏(僵尸线程)。
错误码:只要是pthread开头的函数,返回值都为独立的错误码,而不是直接修改全局变量errno
示例
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>//将错误码转换为字符串必须使用该头文件void * doSomething(void *)
{//sleep(2);pthread_detach(pthread_self());//分离线程,让资源自动释放printf("子线程的线程号:%lu\n", pthread_self());//获得当前线程TIDchar * return_val = "Create Thread Sussess\n";pthread_exit(return_val);//返回程序函数执行结束需要返回的值
}int main(void)
{pthread_t tid;pthread_create(&tid, NULL, doSomething, NULL);//创建一条子线程printf("主线程的线程号:%lu\n", pthread_self());//获取当前线程的TIDsleep(2);void * return_val;int err = pthread_join(tid, &return_val);//获取返回值printf("返回的错误为:%s\n", strerror(err));printf("子线程返回值为:%s\n", (char *)return_val);return 0;}
运行结果
线程的单例
希望某个函数只被严格执行一次,并且需要在程序一开始进行执行,由于线程的并发特性,我们无法预先知晓哪条线程会对信号量进行初始化,于是就希望有一种只执行一遍的函数单例,可以被众多的并发线程放心去调用。
函数接口
#include <pthread.h>// 函数单例控制变量
pthread_once_t once_control = PTHREAD_ONCE_INIT;// 函数单例启动接口
int pthread_once(pthread_once_t *once_control, void (*init_routine)(void));
参数说明
once_control是一种特殊的变量,用来关联某个函数单例,被关联的函数单例只会被执行一遍。
init_routine函数指针指向的函数就是只执行一遍的函数单例。
示例
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>#include <pthread.h>// 函数单例控制变量
pthread_once_t once_control = PTHREAD_ONCE_INIT;void init_routine(void)
{printf("我会被严格执行一遍。\n");
}void *f(void *arg __attribute__((unused)))
{pthread_once(&once_control, init_routine);pthread_exit(NULL);
}int main()
{pthread_t tid;for(int i=0; i<20; i++)pthread_create(&tid, NULL, f, NULL);pthread_exit(NULL);
}
线程的取消
函数API
#include <pthread.h>int pthread_cancel(pthread_t thread);//取消已有的进程
返回值
成功时返回0,失败时返回错误码。
取消状态
可以设置线程是否接收取消,若不接收取消则取消信号无效,接收取消会在取消点进行线程的取消 通过以下API进行设置
int pthread_setcancelstate(int state, int *oldstate);//设置取消状态
int pthread_setcanceltype(int type, int *oldtype);//设置取消类型
取消状态:决定线程是否响应取消请求。
PTHREAD_CANCEL_ENABLE:线程可以响应取消请求(默认)。
PTHREAD_CANCEL_DISABLE:线程忽略取消请求。
取消类型:决定线程如何响应取消请求。
PTHREAD_CANCEL_DEFERRED:线程在下一个取消点(Cancellation Point)检查取消请求(默认)。
PTHREAD_CANCEL_ASYNCHRONOUS:线程可能在任何时间点被取消(不推荐,可能导致资源泄漏)。
清理处理
线程取消时可能需要释放资源(如内存、文件描述符等)。可以通过线程清理处理程序(Thread Cleanup Handlers)实现。清理处理程序是注册的函数,在线程取消或调用pthread_exit时执行。
@ void (*routine)(void *) : 要注册的函数指针
@ void *arg : 传给清理函数的参数
@ int execute :调用时传入非零参数会执行回调函数,传入零参数仅清理而不执行。
void pthread_cleanup_push(void (*routine)(void *), void *arg);//注册清理函数
void pthread_cleanup_pop(int execute);
线程属性
线程有许多属性,可以在终端中查看跟线程属性相关的函数:敲入如下命令后连续按两下tab键
线程属性的使用
#include <pthread.h>int pthread_attr_init(pthread_attr_t *attr);
int pthread_attr_destroy(pthread_attr_t *attr);
- 定义且初始化属性变量 attr
- 将所需的属性,加入 attr 中
- 使用 attr 启动线程
- 销毁 attr
示例
// 初始化属性变量,并将分离属性添加进去pthread_attr_t attr;//创建属性变量pthread_attr_init(&attr);//初始化属性pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);//设置属性pthread_attr_destory(&attr);//销毁属性