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 PrefixFunctional 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);
    
}


`