Pthread使用

POSIX线程

简称Pthreads,是线程POSIX标准。该标准定义了创建和操作线程的一整套API。在类UNIX操作系统中,都使用Pthreads作为操作系统的线程。

API

Pthreads的API函数可以非正式的划分为三大类:

  • 线程管理(Thread Management):直接用于线程创建,分离,连接等。包含了用于设置和查询线程属性(可连接,调度属性等)
  • 互斥量(mutex):用于线程同步,称为互斥量。Mutex函数提供了创建,销毁,锁定和解锁互斥量的功能,同时还包括了一些用于设定或修改互斥量属性的函数
  • 条件变量(condition variables):处理共享一个互斥量的线程间通信,基于程序员指定的条件。这类函数包含执行的条件变量的创建,销毁,等待和受信(signal)
  • 命名约定:线程库中所有标识符否已pthread开头
Routine Prefix Functional Group
pthread_ 线程本身和各种相关函数
pthread_attr_ 线程属性对象
pthread_mutex_ 互斥量
pthread_mutexattr_ 互斥量属性对象
pthread_cond_ 条件变量
pthread_condattr_ 条件变量属性对象
pthread_key_ 线程数据键(Thread-specific data keys)

创建、终止线程

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
#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); //用来等待一个线程的结束。
//参数列表:被等待的线程标识符,用户定义的指针来存储线程的返回值。pthread_join是一个线程阻塞函数,调用他的函数将一直等待到tid线程结束为止。当函数返回时,tid线程的资源被收回。如果成功返回0,失败则返回错误号。
printf("返回%d\n", (int)status);
}

线程同步

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 <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

int i = 0;
pthread_mutex_t mutex; //互斥锁

void* thr_fun(void* arg){
//对1进行操作,出现临界区
//加锁
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);
}

生产者消费者线程

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#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];

//互斥锁
pthread_mutex_t mutex;
//同步锁
pthread_cond_t has_product;

//产品队列
int ready = 0;

//生产
void* producer(void* arg){
int no = (int) arg;
//条件变量
while{
pthread_mutex_lock(&mutex);

ready++;
printf("producer %d, produce product \n", no);
//通知消费者有产品可以消费,唤醒监听has_product的线程
//会阻塞输出
pthread_cond_signal(&has_product);
printf("producer %d, signal\n", no);

pthread_mutex_unlock(&mutex);
sleep(1);
}
}

//消费者
void* consumer(void* arg){
int num = (int) arg;
while{
pthread_mutex_lock(&mutex);

while(ready == 0){
//没有产品,继续等待
//1. 阻塞等待has_product被唤醒
//2. 释放互斥锁,pthread_mutex_unlock
//3. 被唤醒时,接触阻塞,重新申请获得互斥锁
printf("%d consumer wait\n", num);
pthread_cond_wait(&has_product, &mutex);
//pthread_cond_wait做的第一件事情就是对互斥对象解锁,阻塞当前线程等待cond发生
}

//有产品,消费产品
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++){
print("%d\n");
pthread_create(&pid[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);
}

pthread_join和pthread_detach

https://blog.csdn.net/weibo1230123/article/details/81410241

  1. linux线程执行和windows不同,pthread有两种状态joinable状态unjoinable状态,如果线程是joinable状态,当线程函数自己返回退出时或pthread_exit时都不会释放线程所占用堆栈和线程描述符(总计8K多)。只有当你调用了pthread_join之后这些资源才会被释放。若是unjoinable状态的线程,这些资源在线程函数退出时或pthread_exit时自动会被释放。
  2. unjoinable属性可以在pthread_create时指定,或在线程创建后在线程中pthread_detach自己, 如:pthread_detach(pthread_self()),将状态改为unjoinable状态,确保资源的释放。或者将线程置为 joinable,然后适时调用pthread_join.
  3. 简单的说就是在线程函数头加上 pthread_detach(pthread_self())的话,线程状态改变,在函数尾部直接 pthread_exit线程就会自动退出。省去了给线程擦屁股的麻烦。

pthread_join

将子线程合并进入主线程,主线程阻塞等待子线程结束,然后收回子线程资源。pthread_join()函数,以阻塞的方式等待thread指定的线程结束。当函数(pthread_create中指定的线程func)返回时,被等待线程的资源被收回。如果线程已经结束,那么该函数会立即返回。并且thread指定的线程必须是joinable的。

pthread_detach

将子线程与主线程分离,子线程结束后,资源自动回收。
pthread_join()函数的替代函数,可回收创建时detachstate属性设置为PTHREAD_CREATE_JOINABLE的线程的存储空间。该函数不会阻塞父线程。
通常是主线程使用pthread_create()创建子线程以后,一般可以调用pthread_detach(threadid)分离刚刚创建的子线程。