Pthread
来源:互联网 发布:php防止ajax重复提交 编辑:程序博客网 时间:2024/06/05 18:17
Pthread - 互斥量(Mutex) 和 条件变量(Condition variable)
Mutex
Mutex 全称 “mutual exclusion”,是一种实现线程同步的方法。
在访问共享资源时,Mutex 的作用如同一把“锁”。给定时间只有一个线程可以对 Mutex 加锁,其他线程必须等待当前线程解锁才能重新获取 Mutex。如此线程便可以依次访问共享资源。
通常Mutex 使用场景是多个线程访问全局变量(又被称作“critical section”),Mutex可以确保变量的最终值和单线程下操作结果保持一致。
Pthread 中 Mutex 的操作函数有:
pthread_mutexattr_init (attr)
thread_mutex_init (mutex,attr)
pthread_mutex_lock (mutex)
pthread_mutex_trylock (mutex)
pthread_mutex_unlock (mutex)
pthread_mutex_destroy (mutex)
pthread_mutexattr_destroy (attr)
其中pthread_mutexattr_init和pthread_mutexattr_destroy初始化和销毁 Mutex属性变量(pthread_mutexattr_t);pthread_mutex_init和pthread_mutex_destroy初始化和销毁互斥量(pthread_mutex_t); pthread_mutex_lock和 pthread_mutex_trylock尝试对 Mutex 加锁,不同之处在于前者在Mutex 已被其它线程获取会阻塞,而后者会立即返回一个“EBUSY”的错误码。pthread_mutex_unlock对 Mutex 解锁。
下面提供一个示例,程序实现两向量的点积:
#include <pthread.h>#include <stdio.h>#include <stdlib.h>typedef struct { double *a; double *b; double sum; int veclen;}DOTDATA;#define NUMTHRDS 4#define VECLEN 100DOTDATA dotstr;pthread_t callThd[NUMTHRDS];pthread_mutex_t mutexsum;void *dotprod(void *arg){ long offset = (long)arg; int len = dotstr.veclen; int start = offset*len; int end = start + len; double* x = dotstr.a; double* y = dotstr.b; printf("Thread %ld started...\n", offset); double mysum; mysum = 0; int i; for(i=start; i<end; ++i) { mysum += (x[i]*y[i]); } pthread_mutex_lock(&mutexsum); dotstr.sum += mysum; pthread_mutex_unlock(&mutexsum); pthread_exit((void*)0);}int main(int argc, char* argv[]) { double *a = (double*)malloc(NUMTHRDS*VECLEN*sizeof(double)); double *b = (double*)malloc(NUMTHRDS*VECLEN*sizeof(double)); long i; for(i=0; i<VECLEN*NUMTHRDS; ++i) { a[i] = 1.0; b[i] = a[i]; } dotstr.veclen = VECLEN; dotstr.a = a; dotstr.b = b; dotstr.sum = 0; pthread_mutex_init(&mutexsum, NULL); pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); for(i=0; i<NUMTHRDS; ++i) { pthread_create(&callThd[i], &attr, dotprod, (void*)i); } pthread_attr_destroy(&attr); for(i=0; i<NUMTHRDS; ++i) { pthread_join(callThd[i], NULL); } printf("Sum = %f\n", dotstr.sum); free(a); free(b); pthread_mutex_destroy(&mutexsum); pthread_exit(NULL);}
运行效果:
Condition variable
条件变量是另一种实现线程同步的方法,经常和 Mutex 配合使用。常见的应用场景如下:
有关条件变量的操作也与 Mutex 类似:
pthread_cond_init (condition,attr)
pthread_cond_destroy (condition)
pthread_condattr_init (attr)
pthread_condattr_destroy (attr)
pthread_cond_wait (condition,mutex)
pthread_cond_signal (condition)
pthread_cond_broadcast (condition)
这里需要注意的是pthread_cond_wait函数,调用该函数后,线程会阻塞至接收到指定条件的信号为止。而且在调用函数前应该对 Mutex 加锁,在pthread_cond_wait 函数等待期间线程会对 Mutex自动解锁,以便其它线程能够进入临界区,修改条件,在收到指定条件的信号后,线程自动对 Mutex加锁。简而言之:该函数涉及解锁-等待条件信号-加锁三个过程。
在等待条件变化时,推荐使用 WHILE 循环:
1.多个线程等待同一信号时,它们将依次获得 Mutex,然后修改等待条件
2.避免程序收到因程序 Bug 而产生的错误信号
3.Pthread 库可能产生虚假的唤醒信号
以下是一个示例程序:
#include <unistd.h>#include <pthread.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#define NUMTHREADS 3#define TCOUNT 10#define COUNT_LIMIT 12int count = 0;pthread_mutex_t count_mutex;pthread_cond_t count_threshold_cv;void* inc_count(void* t) { long my_id = (long)t; int i; for(i=0; i<TCOUNT; ++i) { pthread_mutex_lock(&count_mutex); count++; if(COUNT_LIMIT == count) { pthread_cond_signal(&count_threshold_cv); printf("inc_count(): thread %ld, count = %d Threshold reached.\n", my_id, count); } printf("inc_count(): thread %ld, count = %d Unlocking mutex.\n", my_id, count); pthread_mutex_unlock(&count_mutex); sleep(1); } pthread_exit(NULL);}void *watch_count(void* t) { long my_id = (long)t; printf("Starting watch_count(): thread %ld\n", my_id); pthread_mutex_lock(&count_mutex); while(count<COUNT_LIMIT) { pthread_cond_wait(&count_threshold_cv, &count_mutex); printf("watch_count(): thread %ld Condition signal received.\n", my_id); } count += 125; printf("watch_count(): thread %ld count now = %d.\n", my_id, count); pthread_mutex_unlock(&count_mutex); pthread_exit(NULL);}int main(int argc, char* argv[]){ pthread_mutex_init(&count_mutex, NULL); pthread_cond_init(&count_threshold_cv, NULL); long t1=1, t2=2, t3=3; pthread_t tid[3]; pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); pthread_create(&tid[0], &attr, watch_count, (void*)t1); pthread_create(&tid[1], &attr, inc_count, (void*)t2); pthread_create(&tid[2], &attr, inc_count, (void*)t3); pthread_attr_destroy(&attr); long i; for(i=0; i<NUMTHREADS; ++i) pthread_join(tid[i], NULL); printf("Main(): Waited on %d threads to finish.\n", NUMTHREADS); pthread_mutex_destroy(&count_mutex); pthread_cond_destroy(&count_threshold_cv); pthread_exit(NULL);}
运行效果如下:
在了解条件变量以后,我们可以对用条件变量实现上一篇博客里的生产者消费者模型。代码如下:
#include <unistd.h>#include <errno.h>#include <pthread.h>#include <semaphore.h>#include <fcntl.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#define ERROR(func, no) { \ fprintf(stderr, "%s: %s\n", func, strerror(no)); \ exit(EXIT_FAILURE); \ }#define DEFAULT_CONSUMER_CNT 1#define DEFAULT_PRODUCER_CNT 3static size_t consumer_cnt; // count of consumersstatic size_t producer_cnt; // count of producersstatic size_t cake_cnt;static pthread_t* g_thread = NULL; // pointer to thread IDsstatic pthread_mutex_t g_mutex; //mutexstatic pthread_cond_t g_cond; // condition variablevoid* consume(void* arg) { int id = (int)arg; while(1) { pthread_mutex_lock(&g_mutex); while(cake_cnt <= 0) { // Consumer must wait untill there is one cake at least printf("Consumer[%d] waiting for cakes\n", id); pthread_cond_wait(&g_cond, &g_mutex); } printf("Consumer[%d] consuming a cake\n", id); cake_cnt--; pthread_mutex_unlock(&g_mutex); sleep(1); } return NULL;}void* produce(void* arg) { int id = (int)arg; while(1) { pthread_mutex_lock(&g_mutex); ++cake_cnt; printf("Producer[%d] makes a cake...\n", id); pthread_cond_signal(&g_cond); pthread_mutex_unlock(&g_mutex); sleep(1); } return NULL;}int main(int argc, char** argv){ consumer_cnt = DEFAULT_CONSUMER_CNT; producer_cnt = DEFAULT_PRODUCER_CNT; char* prog = argv[0]; int ch; while ((ch = getopt(argc, argv, "b:c:p:")) != -1) { switch (ch) { case 'c': consumer_cnt = atoi(optarg); break; case 'p': producer_cnt = atoi(optarg); break; case '?': default: printf("Usage: %s [-p producer_cnt] [-c consumer_cnt]\n" "\tdefault producer_cnt=3, consumer_cnt=1\n", prog); exit(EXIT_FAILURE); } } g_thread = (pthread_t*)malloc((consumer_cnt+producer_cnt)*sizeof(pthread_t)); memset(g_thread, 0, (consumer_cnt+producer_cnt)*sizeof(pthread_t)); pthread_mutex_init(&g_mutex, NULL); pthread_cond_init(&g_cond, NULL); pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); int i, ret; for(i=0; i<consumer_cnt; ++i) { ret = pthread_create(&g_thread[i], &attr, consume, (void*)i); if(ret) { ERROR("pthread_create", ret); } } for(i=0; i<producer_cnt; ++i) { ret = pthread_create(&g_thread[i+consumer_cnt], &attr, produce, (void*)i); if(ret) { ERROR("pthread_create", ret); } } for(i=0; i<consumer_cnt+producer_cnt; ++i) { ret = pthread_join(g_thread[i], NULL); if(ret) { ERROR("pthread_join", ret); } } pthread_mutex_destroy(&g_mutex); pthread_cond_destroy(&g_cond); free(g_thread); exit(EXIT_SUCCESS);}
由于使用条件变量实现时采用无界缓冲区,故而在只需在当前 buffer 为空时,让消费者线程等待即可,所以实现起来与semaphore相比要简单许多。运行效果:
当然,你可以调整生产者和消费者的数量,观察程序的不同结果,加深对该模型的理解。
- Pthread
- pthread
- pthread
- PThread
- pthread
- pthread
- pthread
- pthread
- pthread
- pthread
- pthread
- pthread
- pthread
- pthread
- Pthread
- pthread
- pthread
- Pthread
- 一道前端面试题:用原生JS实现,点击按钮,alert-button的内容
- 数组去重的正则表达式写法
- ThinkSNS+ 基于 Laravel master 分支,从 1 到 0,再到 0.1
- ssh2框架整合详细版(使用较新jar包)
- 黑马程序员——Java基础---反射Class类、Constructor类、Field类
- Pthread
- 自定义约束下的cell的高度
- Linux date
- 【微服务】Springcloud学习笔记(一) —— Eureka
- 避免U盘中毒的方法
- PHPExcel export网络或本地图片到excel
- Angularjs中的事件广播(全面解析$broadcast,$emit,$on)
- 第八届省赛题 引水工程
- Storm BaseBasicBolt和BaseRichBolt。