前言

本文仅针对pthread库的使用做出必要 指导 ,并且居然较大的主观性。观看前默认阅读者已经知晓 进程、线程以及多线程的相关基础知识,具体可以参阅站内文章:

本文介绍的pthread是基于 POSIX 操作系统的多线程API,事实上,在C++11之后的标准库中已经集成了多线程库std::thread,但本文并未涉及。

POSIX 表示可移植操作系统接口 ( Portable Operating System Interface of UNIX,缩写为 POSIX )

目前主流的 LinuxMac OS 都属于 POSIX 系统

值得注意的是,Windows系统并不是POSIX类型的操作系统,因此想要在windows系统上使用pthread需要安装相应的链接库。如果你没有这方面的困扰,就跳过该部分。

在Windows下配置pthread

pthreads-w32 下载

进入官网下载对应的pthreads-w32资源:

配置头文件及静态链接库

待更,见文末参考资料

快速使用

引用

  1. 基于 POSIX 开发多线程程序需要加载 <pthread.h>

    1
    #include <pthread.h>
  2. pthread 提供了一个类型 pthread_t 用来表示一个线程

  3. pthread 库不是 LinuxMac OS 系统默认的库,编译连接时需要加上静态库 libpthread.a,例如:

    1
    g++ main.cpp -lpthread
VScode中配置链接库

待更

创建线程

pthread.h 提供了 pthread_create() 函数用于创建一个 POSIX 线程,并立即让它执行

1
pthread_create (thread, attr, start_routine, arg)

创建线程成功时,函数返回 0,若返回值不为 0 则说明创建线程失败

参数描述
thread指向线程标识符指针
attr一个不透明的属性对象,用来设置线程属性 可以指定线程属性对象,也可以传递 NULL
start_routine线程运行函数起始地址,一旦线程被创建就会执行
arg运行函数的参数,它必须通过把引用作为指针强制转换为 void 类型进行传递 如果没有传递参数,可以使用 NULL

终止线程

pthread_exit() 函数可以用来显式地终止一个 POSIX 线程。通常情况下,pthread_exit() 函数是在线程完成工作后无需继续存在时被调用。

1
pthread_exit (status)

如果 main() 是在它所创建的线程之前结束,并通过 pthread_exit() 退出,那么其他线程将继续执行。否则,它们将在 main() 结束时自动被终止

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>
#include <pthread.h>
using namespace std;

//自定义的线程函数
void* helloworld(void* args){
cout << "hello,i am a thread code!" << endl;
return NULL;
}

//主函数
int main(void){

//定义线程的id变量
pthread_t tid;

//创建线程
//参数依次是:创建的线程id,线程参数,调用的函数,传入的函数参数
int ret = pthread_create(&tid,NULL,helloworld,NULL);

//考虑创建失败的情况
if(ret != 0){
cout << "Error:线程创建失败 [error code:" << ret << "]" << endl;
exit(-1);
}

//等线程退出后,进程才结束。
//否则进程强制结束,即主程序直接return 0后,线程可能还没运行结束就被强制结束;
pthread_exit(NULL);
}

传递参数

在上文中,我们了解到pthread_create()函数可以传入参数给线程函数,而线程函数如helloworld()可以传入void* args参数。

此外,可以见得:传入的参数被限定为void*类型,这是pthread的规定。

因此,综合以上几点我们可以利用C++的类型转换传递给线程函数几乎所有数据结构的参数,具体操作方式如下:

  1. 定义需要传递参数的结构体,并在main()中创建实例;
  2. 实例的指针强制转换为void*类型,作为参数传递给pthread_create()
  3. 在具体的线程函数中,将接受到的args指针再强制转换为原来的结构体类型的指针从而进行数据还原

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <iostream>
#include <pthread.h>

using namespace std;

//线程数据
typedef struct thread_data{
int thread_id;
char* message;
}threadData;

//线程函数
void* getMessage(void* args){
threadData* myGetData;
myGetData = (threadData*)args;//将数据指针转换回正确的指针类型
cout << "线程ID:" << myGetData->thread_id;
cout << ",接收消息为:" << myGetData->message << endl;
pthread_exit(NULL);
}
//主函数
int main(void){
pthread_t tid[3];//创建3个线程
threadData args[3] = {
{1,(char*)"aaa"},
{2,(char*)"bbb"},
{3,(char*)"ccc"}
};//创建3个线程应该接收的参数
//分别创建线程
for(int i = 0; i < 3; i++){
//将参数的指针转为void*类型
int ret = pthread_create(&tid[i],NULL,getMessage,(void*)&args[i]);
if(ret){
cout << "Error:线程创建失败 [error code:" << ret << "]" << endl;
exit(-1);
}
}
pthread_exit(NULL);
}

结果如下:

1
2
3
4
5
6
$ g++ -g test.cpp -lpthread -o test
$ ./test

线程ID:2,接收消息为:bbb
线程ID:1,接收消息为:aaa
线程ID:3,接收消息为:ccc

连接与分离

待更

进程同步

互斥与同步机制基本函数

函数说明
pthread_mutex_init()互斥锁的初始化
pthread_mutex_lock()锁定互斥锁,如果尝试锁定已经被上锁的互斥锁则阻塞至可用为止
pthread_mutex_trylock()非阻塞的锁定互斥锁
pthread_mutex_unlock()释放互斥锁
pthread_mutex_destory()互斥锁销毁函数

信号量线程控制(默认无名信号量)

函数说明
sem_init(sem)初始化一个定位在sem的匿名信号量
sem_wait()把信号量减1操作,如果信号量的当前值为0则进入阻塞,为原子操作
sem_trywait()如果信号量的当前值为0则返回错误而不是阻塞调用(errno=EAGAIN),其实是sem_wait()的非阻塞版本
sem_post()给信号量的值加1,它是一个“原子操作”,即同时对同一个信号量做加1,操作的两个线程是不会冲突的
sem_getvalue(sval)把sem指向的信号量当前值放置在sval指向的整数上
sem_destory(sem)销毁由sem指向的匿名信号量

参考资料

1.在windows下配置pthread|CSDN

2.C++ 多线程|菜鸟教程

3.pthread详解|CSDN

4.C/C++ Pthread线程|张大猛,博客园