pthread 使用入门
原文链接: pthread 使用入门
POSIX 线程(POSIX threads),简称 pthreads,是线程的 POSIX 标准。该标准定义了创建和操纵线程的一整套 API。在类 Unix 操作系统(Unix、Linux、Mac OS X 等)中,都使用 pthreads 作为操作系统的线程。Windows 操作系统也有其移植版 pthreads-win32
API
一、 Pthreads API 中的函数可以非正式的划分为三大类:
- 线程管理(Thread management): 第一类函数直接用于线程:创建(creating),分离(detaching),连接(joining)等等。包含了用于设置和查询线程属性(可连接,调度属性等)的函数。
- 互斥量(Mutexes): 第二类函数是用于线程同步的,称为互斥量(mutexes),是 "mutual exclusion" 的缩写。
- Mutex 函数提供了创建,销毁,锁定和解锁互斥量的功能。同时还包括了一些用于设定或修改互斥量属性的函数。
- 条件变量(Condition variables):第三类函数处理共享一个互斥量的线程间的通信,基于程序员指定的条件。这类函数包括指定的条件变量的创建,销毁,等待和受信(signal)。设置查询条件变量属性的函数也包含其中。
- 命名约定:线程库中的所有标识符都以 pthread 开头
Routine Prefix | Functional Group |
---|---|
pthread_ t | 线程本身和各种相关函数 |
pthread_attr_t | 线程属性对象 |
pthread_mutex_t | 互斥量 mutex |
pthread_mutexattr_t | 互斥量属性对象 |
pthread_cond_t | 条件变量 cond |
pthread_condattr_t | 条件变量属性对象 |
pthread_key_t | 线程数据键(Thread-specific data keys) |
- 在 API 的设计中充满了不透明对象的概念,基本调用可以创建或修改不透明对象。不透明的对象可以被一些属性函数调用修改。
pthread_cond_t 条件变量
使用场景:
条件变量比较罕见,它和信号量有些相似,也是线程同步的一种方式,但是不同的地方是条件变量具有广播的功能,比如n个线程在等待一个条件变量时进行广播就能同时唤醒这n个进程。当然,条件变量也能用于不同进程中,需要一些技巧,暂时不讲。条件变量的适用有点繁,还需要一个额外的互斥量来配合使用,因为条件变量的接口并不是线程安全的。
条件变量的接口:
#include <pthread.h>
int pthread_cond_timedwait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex,
const struct timespec *restrict abstime);
int pthread_cond_wait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex);
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
wait接口是阻塞地等待,而timedwait接口可以指定最多等待多久时间,防止永久等待。
signal函数就是只能唤醒单个等待的线程,具体是哪个线程可以认为是随机的,而broadcast函数可以唤醒所有等待的线程,推荐使用broadcast接口。
下面是条件变量创建和销毁的接口:
#include <pthread.h>
// 1. 创建一个条件
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
int pthread_cond_destroy(pthread_cond_t *cond);
常见的使用方法:
#include <pthread.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
// 线程A
int pthreadA()
{
pthread_mutex_lock(mutex);
pthread_cond_wait(cond);
pthread_mutex_unlock(mutex);
return 0;
}
// 线程B
int pthreadB()
{
pthread_mutex_lock(mutex);
pthread_cond_signal(cond);
pthread_mutex_unlock(mutex);
return 0;
}
条件变量和信号量的区别是明显的,条件变量还得配合互斥量,很繁琐,而且它不能代替信号量。考虑这样的一种场景,当前没有进程在等待,调用pthread_cond_signal会怎样?啥事都没发生,下次有线程调用pthread_cond_wait的话,也是需要等待的。但是信号量就不是这样了,释放信号量是可以累加的,下次有线程需要获取信号量的时候就无需等待立即获得,且扣减信号量。
例子
一、 创建 / 终止线程
创建一个文件 pthreadTest1
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void* xc(void* arg){
char* c=(char*)arg;
printf("参数%s \n",c);
int i=0;
for (;i<10;i++){
printf("循环%d\n",i);
if(i==5){
pthread_exit(1090000000);
}
}
return 100000222;
}
void main(){
pthread_t tid;
pthread_create(&tid,NULL,xc,"线程!!!!");
void *status;
pthread_join(tid,&status);
printf("返回%d\n",(int)status);
}
编译
gcc pthreadTest1.c -o pthreadTest1 -lpthread
运行
结果:
参数线程!!!!
循环0
循环1
循环2
循环3
循环4
循环5
返回1090000000
二、线程同步
创建一个文件 pthreadTest2
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
int i = 0;
pthread_mutex_t mutex;
void* thr_fun(void* arg){
pthread_mutex_lock(&mutex);
char* no = (char*)arg;
for(;i < 5; i++){
printf("%s thread, i:%d\n",no,i);
sleep(1);
}
i=0;
pthread_mutex_unlock(&mutex);
}
void main(){
pthread_t tid1, tid2;
pthread_mutex_init(&mutex,NULL);
pthread_create(&tid1,NULL,thr_fun,"No1");
pthread_create(&tid2,NULL,thr_fun,"No2");
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
pthread_mutex_destroy(&mutex);
}
运行结果
No2 thread, i:0
No2 thread, i:1
No2 thread, i:2
No2 thread, i:3
No2 thread, i:4
No1 thread, i:0
No1 thread, i:1
No1 thread, i:2
No1 thread, i:3
No1 thread, i:4
三、生产消费者线程
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#define CONSUMER_NUM 2
#define PRODUCER_NUM 1
pthread_t pids[CONSUMER_NUM+PRODUCER_NUM];
int ready = 0;
pthread_mutex_t mutex;
pthread_cond_t has_product;
void* producer(void* arg){
int no = (int)arg;
for(;;){
pthread_mutex_lock(&mutex);
ready++;
printf("producer %d, produce product\n",no);
pthread_cond_signal(&has_product);
printf("producer %d, singal\n",no);
pthread_mutex_unlock(&mutex);
sleep(1);
}
}
void* consumer(void* arg){
int num = (int)arg;
for(;;){
pthread_mutex_lock(&mutex);
while(ready==0){
printf("%d consumer wait\n",num);
pthread_cond_wait(&has_product,&mutex);
}
ready--;
printf("%d consume product\n",num);
pthread_mutex_unlock(&mutex);
sleep(1);
}
}
void main(){
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&has_product,NULL);
printf("init\n");
int i;
for(i=0; i<PRODUCER_NUM;i++){
printf("%d\n",i);
pthread_create(&pids[i],NULL,producer,(void*)i);
}
for(i=0; i<CONSUMER_NUM;i++){
pthread_create(&pids[PRODUCER_NUM+i],NULL,consumer,(void*)i);
}
sleep(10);
for(i=0; i<PRODUCER_NUM+CONSUMER_NUM;i++){
pthread_join(pids[i],NULL);
}
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&has_product);
}