Linux下Pthread基本使用
POSIX线程(POSIX threads),简称pthreads,是线程的POSIX标准。该标准定义了创建和操纵线程的一整套API。在类Unix操作系统(Unix、Linux、Mac OS X等)中,都使用 pthreads
作为操作系统的线程。
pthread
不是Linux下的默认的库,所以在Linux环境下,有的有些系统没有安装有 pthread
,所以在编译有关多线程程序时,也就是在程序链接的时候,无法找到 pthread
库中函数的入口地址,于是链接会失败。所以在此我们必须先安装好 pthread
库。安装过程很简单使用下面命令即可。
sudo apt-get install manpages-posix-dev
然后再用 man -k pthread
就可以查找到当前 manual
中关于 pthread
的手册,按 q
可退出。
#include <stdio.h>
#include <pthread.h>
void* thr_fun(void* arg){
printf("new thread\n");
char* num = (char*)arg;
int i;
for(i = 0; i < 10; i++){
printf("%s thread, i:%d\n", num, i);
}
}
void main(){
printf("main thread\n");
// 线程id
pthread_t tid;
// 参数2:线程的属性,NULL默认属性
// 参数3:线程创建之后执行的函数
// 参数4:thr_fun函数接受的参数
pthread_create(&tid, NULL, thr_fun ,"arg参数");
}
编译 .c
文件,可直接生成可执行文件,省略中间 .o
文件的步骤。
gcc 01.c -o 01
报错
/tmp/ccC0AheU.o: In function `main':
01.c:(.text+0x7b): undefined reference to `pthread_create'
collect2: error: ld returned 1 exit status
使用 pthread
需要在编译时需要添加 -lpthread
,因为 pthread
库不是 Linux 系统默认的库,连接时需要使用静态库 libpthread.a
,所以在编译的时候需要链接该库,重新编译,可生成可执行文件01。
gcc 01.c -o 01 -lpthread
执行01文件,只输出了 main thread
。
-
由于创建新线程后,main函数立马结束,程序退出,所以可以在main结束前
sleep
。#include <stdio.h> #include <pthread.h> #include <unistd.h> void* thr_fun(void* arg){ printf("new ithread\n"); char* num = (char*)arg; int i; for(i = 0; i < 10; i++){ printf("%s thread, i:%d\n", num, i); } } void main(){ printf("main thread\n"); // 线程id pthread_t tid; // 参数2:线程的属性,NULL默认属性 // 参数3:线程创建之后执行的函数 // 参数4:thr_fun函数接受的参数 pthread_create(&tid, NULL, thr_fun ,"arg参数"); sleep(1); }
打印结果
main thread new ithread arg参数 thread, i:0 arg参数 thread, i:1 arg参数 thread, i:2 arg参数 thread, i:3 arg参数 thread, i:4 arg参数 thread, i:5 arg参数 thread, i:6 arg参数 thread, i:7 arg参数 thread, i:8 arg参数 thread, i:9
-
但是这种方式并不可控,应该使用
pthread_join
函数,它会阻塞等待指定id的线程结束,执行结果是一样的。#include <stdio.h> #include <pthread.h> void* thr_fun(void* arg){ printf("new ithread\n"); char* num = (char*)arg; int i; for(i = 0; i < 10; i++){ printf("%s thread, i:%d\n", num, i); } } void main(){ printf("main thread\n"); // 线程id pthread_t tid; // 参数2:线程的属性,NULL默认属性 // 参数3:线程创建之后执行的函数 // 参数4:thr_fun函数接受的参数 pthread_create(&tid, NULL, thr_fun ,"arg参数"); pthread_join(tid, NULL); }
-
pthread_join
第二个参数是thr_fun退出时的参数,return
的返回值或者pthread_exit
的参数。#include <stdio.h> #include <pthread.h> void* thr_fun(void* arg){ printf("new ithread\n"); char* num = (char*)arg; int i; for(i = 0; i < 10; i++){ printf("%s thread, i:%d\n", num, i); if(i == 5){ // 线程退出(自杀) pthread_exit((void*)5); // 他杀:int pthread_cancel(pthread_t thread); } } return (void*)1; } void main(){ printf("main thread\n"); // 线程id pthread_t tid; // 参数2:线程的属性,NULL默认属性 // 参数3:线程创建之后执行的函数 // 参数4:thr_fun函数接受的参数 pthread_create(&tid, NULL, thr_fun ,"arg参数"); void* rval; // 等待tid线程结束 // 参数2:thr_fun退出时传入的参数,return或者pthread_exit pthread_join(tid, &rval); printf("rval:%s\n", (int)rval); }
-
thr_fun
退出时,不调用return
和pthread_exit
,打印rval是没有意义的,甚至还会报错。
当两个线程同时处理
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
int i = 0;
void* thr_fun(void* arg){
char* num = (char*)arg;
for(; i < 5; i++){
printf("%s thread, i:%d\n", num, i);
sleep(1);
}
i = 0;
}
void main(){
pthread_t tid1, tid2;
pthread_create(&tid1, NULL, thr_fun, "No1");
pthread_create(&tid2, NULL, thr_fun, "No2");
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
}
打印结果
No2 thread, i:0
No1 thread, i:0
No2 thread, i:1
No1 thread, i:2
No2 thread, i:3
No1 thread, i:4
No1 thread, i:1
No1 thread, i:2
No1 thread, i:3
No1 thread, i:4
我们的目标是,让两个线程互不影响,让一个线程先做完,再让另一个线程去做。 使用互斥锁解决问题。
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
int i = 0;
// 互斥锁
pthread_mutex_t mutex;
void* thr_fun(void* arg){
// 加锁
pthread_mutex_lock(&mutex);
char* num = (char*)arg;
for(; i < 5; i++){
printf("%s thread, i:%d\n", num, 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 <stdio.h> #include <pthread.h> #include <unistd.h> // 产品队列 int ready = 0; // 互斥锁 pthread_mutex_t mutex; // 条件变量 pthread_cond_t has_product; // 生产 void* producer(void* arg){ char* num = arg; // 条件变量 for(;;){ pthread_mutex_lock(&mutex); // 往队列中添加产品 ready++; printf("producer %s, produce product\n", num); // 通知消费者,有新的产品可以消费了 pthread_cond_signal(&has_product); printf("producer %s, signal\n", num); pthread_mutex_unlock(&mutex); sleep(1); } } // 消费者 void* consumer(void* arg){ char* num = arg; for(;;){ pthread_mutex_lock(&mutex); // 不能使用if,因为存在spurious wakeup ‘虚假唤醒’ while(ready == 0){ // 没有产品,继续等待 pthread_cond_wait(&has_product, &mutex); printf("consumer %s, wait\n", num); } // 有产品,消费产品 ready--; printf("consumer %s, consume produce\n", num); pthread_mutex_unlock(&mutex); sleep(1); } } void main(){ pthread_t tid_p, tid_c; // 初始化互斥锁和条件变量 pthread_mutex_init(&mutex, NULL); pthread_cond_init(&has_product, NULL); // 生产者线程 pthread_create(&tid_p, NULL, producer, "1"); // 消费者线程 pthread_create(&tid_c, NULL, consumer, "2"); // 等待 pthread_join(tid_p, NULL); pthread_join(tid_c, NULL); pthread_mutex_destroy(&mutex); pthread_cond_destroy(&has_product); }
打印结果
producer 1, produce product producer 1, signal consumer 2, wait consumer 2, consume produce producer 1, produce product producer 1, signal consumer 2, wait consumer 2, consume produce producer 1, produce product producer 1, signal consumer 2, wait consumer 2, consume produce
-
生产大于消费
#include <stdio.h> #include <pthread.h> #include <unistd.h> // 消费者数量 #define CONSUMER_NUM 1 // 生产者数量 #define PRODUCER_NUM 2 // 线程id数组 pthread_t pids[CONSUMER_NUM + PRODUCER_NUM]; // 产品队列 int ready = 0; // 互斥锁 pthread_mutex_t mutex; // 条件变量 pthread_cond_t has_product; // 生产 void* producer(void* arg){ int num = (int)arg; // 条件变量 for(;;){ pthread_mutex_lock(&mutex); // 往队列中添加产品 ready++; printf("producer %d, produce product\n", num); // 通知消费者,有新的产品可以消费了 pthread_cond_signal(&has_product); printf("producer %d, signal\n", num); pthread_mutex_unlock(&mutex); sleep(1); } } // 消费者 void* consumer(void* arg){ int num = (int)arg; for(;;){ pthread_mutex_lock(&mutex); // 不能使用if,因为存在spurious wakeup ‘虚假唤醒’ while(ready == 0){ // 没有产品,继续等待 // 1.阻塞等待has_product被唤醒 // 2.释放互斥锁,pthread_mutex_unlock // 3.被唤醒时,解除阻塞,重新申请获得互斥锁pthread_mutex_lock pthread_cond_wait(&has_product, &mutex); printf("consumer %d, wait\n", num); } // 有产品,消费产品 ready--; printf("consumer %d, consume produce\n", num); pthread_mutex_unlock(&mutex); sleep(1); } } void main(){ // 初始化互斥锁和条件变量 pthread_mutex_init(&mutex, NULL); pthread_cond_init(&has_product, NULL); int i = 0; // 生产者线程 for(; i < PRODUCER_NUM; i++){ printf("producer %d\n", i); pthread_create(&pids[i], NULL, producer, (void*)i); } // 消费者线程 for(; i < CONSUMER_NUM + PRODUCER_NUM; i++){ printf("consumer %d\n", i); pthread_create(&pids[i], NULL, consumer, (void*)i); } // 等待 for(i = 0; i < CONSUMER_NUM + PRODUCER_NUM; i++){ pthread_join(pids[i], NULL); } // 销毁互斥锁和条件变量 pthread_mutex_destroy(&mutex); pthread_cond_destroy(&has_product); }
打印结果
producer 0 producer 1 consumer 2 producer 1, produce product producer 1, signal consumer 2, wait consumer 2, consume produce producer 0, produce product producer 0, signal consumer 2, consume produce producer 1, produce product producer 1, signal producer 0, produce product producer 0, signal consumer 2, consume produce producer 1, produce product producer 1, signal producer 0, produce product producer 0, signal
-
生产小于消费
// 消费者数量 #define CONSUMER_NUM 2 // 生产者数量 #define PRODUCER_NUM 1
打印结果
producer 0 consumer 1 consumer 2 producer 0, produce product producer 0, signal consumer 2, wait consumer 2, consume produce producer 0, produce product producer 0, signal consumer 1, wait consumer 1, consume produce producer 0, produce product producer 0, signal consumer 2, wait consumer 2, consume produce producer 0, produce product producer 0, signal consumer 1, wait consumer 1, consume produce