线程同步---条件变量

来源:互联网 发布:加藤非 淘宝推荐 编辑:程序博客网 时间:2024/04/30 03:24

1. 问题引入:互斥锁问题,假设现在有两个资源A和B,一个线程先拿A再拿B,另一个则相反,这样导致的问题就是死锁,即两个线程无休止的互相等待

#include <stdio.h>#include <string.h>#include <unistd.h>#include <pthread.h>pthread_mutex_t g_mtxa = PTHREAD_MUTEX_INITIALIZER;pthread_mutex_t g_mtxb = PTHREAD_MUTEX_INITIALIZER;void* thread_proc1 (void* arg) {pthread_t tid = pthread_self ();printf ("%lu线程:等待A...\n", tid);pthread_mutex_lock (&g_mtxa);printf ("%lu线程:获得A!\n", tid);sleep (1);printf ("%lu线程:等待B...\n", tid);pthread_mutex_lock (&g_mtxb);printf ("%lu线程:获得B!\n", tid);pthread_mutex_unlock (&g_mtxb);printf ("%lu线程:释放B。\n", tid);pthread_mutex_unlock (&g_mtxa);printf ("%lu线程:释放A。\n", tid);return NULL;}void* thread_proc2 (void* arg) {pthread_t tid = pthread_self ();printf ("%lu线程:等待B...\n", tid);pthread_mutex_lock (&g_mtxb);printf ("%lu线程:获得B!\n", tid);sleep (1);printf ("%lu线程:等待A...\n", tid);pthread_mutex_lock (&g_mtxa);printf ("%lu线程:获得A!\n", tid);pthread_mutex_unlock (&g_mtxa);printf ("%lu线程:释放A。\n", tid);pthread_mutex_unlock (&g_mtxb);printf ("%lu线程:释放B。\n", tid);return NULL;}int main (void) {pthread_t tid1;int error = pthread_create (&tid1, NULL, thread_proc1, NULL);if (error) {fprintf (stderr, "pthread_create: %s\n", strerror (error));return -1;}pthread_t tid2;if ((error = pthread_create (&tid2, NULL, thread_proc2,NULL)) != 0) {fprintf (stderr, "pthread_create: %s\n", strerror (error));return -1;}if ((error = pthread_join (tid1, NULL)) != 0) {fprintf (stderr, "pthread_join: %s\n", strerror (error));return -1;}if ((error = pthread_join (tid2, NULL)) != 0) {fprintf (stderr, "pthread_join: %s\n", strerror (error));return -1;}printf ("大功告成!\n");return 0;}

思考:解决上面问题的核心就是让一个资源同时使用A和B,使用完在释放,这样就不会出现互相等待导致死锁的问题。


2.  条件变量:条件变量是一种同步机制,允许线程挂起,直到共享数据上的某些条件得到满足。条件变量上的基本操作有:触发条件(当条件变为 true 时);等待条件,挂起线程直到其他线程触发条件。条件变量必须和互斥锁一起使用

int pthread_cond_init (pthread_cond_t* cond,const pthread_condattr_t* attr);// 初始化条件变量pthread_cond_t cond = PTHREAD_COND_INITIALIZER;// 作用和上个函数等价
int pthread_cond_wait (pthread_cond_t* cond,pthread_mutex_t* mutex);// 使调用线程睡入条件变量cond,同时释放互斥锁mutex,int pthread_cond_timedwait (pthread_cond_t* cond, pthread_mutex_t* mutex, const struct timespec* abstime);// abstime 指定的时间内 cond 未触发,互斥量 mutex 被重新加锁struct timespec {    time_t tv_sec;  // Seconds    long   tv_nsec; // Nanoseconds [0 - 999999999]};
int pthread_cond_signal (pthread_cond_t* cond);// 从条件变量cond中唤出一个线程, 令其重新获得原先的互斥锁注意:被唤出的线程此刻将从pthread_cond_wait函数中返回,但如果该线程无法获得原先的锁,则会继续阻塞在加锁上。
int pthread_cond_broadcast (pthread_cond_t* cond);// 从条件变量cond中唤出所有线程
int pthread_cond_destroy (pthread_cond_t* cond);//销毁条件变量释放资源


3. 生产者消费者模型:两个生产 者生产A-Z的字符,两个消费者线程

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <pthread.h>#define MAX_STOCK 20 // 仓库容量char g_storage[MAX_STOCK]; // 仓库size_t g_stock = 0; // 当前库存pthread_mutex_t g_mtx = PTHREAD_MUTEX_INITIALIZER;pthread_cond_t g_full = PTHREAD_COND_INITIALIZER;  // 满仓pthread_cond_t g_empty = PTHREAD_COND_INITIALIZER; // 空仓// 显示库存void show (const char* who, const char* op, char prod) {printf ("%s:", who);size_t i;for (i = 0; i < g_stock; i++)printf ("%c", g_storage[i]);printf ("%s%c\n", op, prod);}// 生产者线程void* producer (void* arg) {const char* who = (const char*)arg;for (;;) {pthread_mutex_lock (&g_mtx);//if (g_stock >= MAX_STOCK) {    //此时应该对条件进行再判断while (g_stock >= MAX_STOCK) {printf ("%s:满仓!\n", who);pthread_cond_wait (&g_full, &g_mtx);}char prod = 'A' + rand () % 26;show (who, "<-", prod);g_storage[g_stock++] = prod;pthread_cond_broadcast (&g_empty);pthread_mutex_unlock (&g_mtx);usleep ((rand () % 100) * 1000);}return NULL;}// 消费者线程void* customer (void* arg) {const char* who = (const char*)arg;for (;;) {pthread_mutex_lock (&g_mtx);//if (! g_stock) {while (! g_stock) {printf ("%s:空仓!\n", who);pthread_cond_wait (&g_empty, &g_mtx);}char prod = g_storage[--g_stock];show (who, "->", prod);pthread_cond_broadcast (&g_full);pthread_mutex_unlock (&g_mtx);usleep ((rand () % 100) * 1000);}return NULL;}int main (void) {srand (time (NULL));pthread_attr_t attr;int error = pthread_attr_init (&attr);if (error) {fprintf (stderr, "pthread_attr_init: %s\n", strerror (error));return -1;}if ((error = pthread_attr_setdetachstate (&attr,//修改线程分离状态属性PTHREAD_CREATE_DETACHED)) != 0) {fprintf (stderr, "pthread_attr_setdetachstate: %s\n",strerror (error));return -1;}pthread_t tid;if ((error = pthread_create (&tid, &attr, producer,"生产者1")) != 0) {fprintf (stderr, "pthread_create: %s\n", strerror (error));return -1;}if ((error = pthread_create (&tid, &attr, producer,"生产者2")) != 0) {fprintf (stderr, "pthread_create: %s\n", strerror (error));return -1;}if ((error = pthread_create (&tid, &attr, customer,"消费者1")) != 0) {fprintf (stderr, "pthread_create: %s\n", strerror (error));return -1;}if ((error = pthread_create (&tid, &attr, customer,"消费者2")) != 0) {fprintf (stderr, "pthread_create: %s\n", strerror (error));return -1;}getchar ();return 0;}
注意:

1.处于分离状态的线程终止后自动释放线程资源,且不能被pthread_join函数等待

2. 进行条件再判断的原因:两个生产者,两个消费者,signal一次只能叫醒一个,所以现在使用broadst广播出去假设现在生产者生产了一个产品,他就广播一下,这时候俩消费者都醒了,其中一个拿到锁然后消耗了资源,再解锁,这时候另一个直接拿到锁,然而此时已经没产品,溢出导致段错误(如下图)。这就是条件被别人消耗了的导致的情况。


4.哲学家就餐问题:

五个哲学家围坐在一张圆桌周围,每个哲学家面前都有一盘通心粉。由于通心粉很滑,所以需要两把叉子才能夹住。相邻两个盘子之间放有一把叉子。哲学家的生活中有两种交替活动时段:即吃饭和思考。当一个哲学家觉得饿了时,他就试图分两次去取其左边和右边的叉子,每次拿一把,但不分次序。如果成功地得到了两把叉子,就开始吃饭,吃完后放下叉子继续思考。那么我们要做的就是为每一个哲学家写一段描述其行为的程序,且决不会死锁吗?

解决方案:让哲学家不看筷子看人,如果两边的人都没吃,就自己吃,吃完后就通知两边的人。

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <pthread.h>#define DINERS 5 // 就餐人数// 就餐者状态typedef enum tag_State {THINKING, // 正在思考HUNGRY,   // 正在挨饿EATING    // 正在吃饭}ESTAT;pthread_mutex_t g_mtx = PTHREAD_MUTEX_INITIALIZER;// 就餐者条件变量数组pthread_cond_t g_conds[DINERS] = {PTHREAD_COND_INITIALIZER, PTHREAD_COND_INITIALIZER,PTHREAD_COND_INITIALIZER, PTHREAD_COND_INITIALIZER,PTHREAD_COND_INITIALIZER};// 就餐者姓名数组const char* g_names[DINERS] = {"哲学家1", "<span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px;">哲学家2</span><span style="font-size: 12px; font-family: Arial, Helvetica, sans-serif;">", "哲学家3", "哲学家4", "哲学家5"};</span>// 就餐者状态数组ESTAT g_stats[DINERS] = {THINKING, THINKING, THINKING, THINKING, THINKING};// 吃饭void eat (int i) {int l = (i + 1) % DINERS; // 左邻int r = (i + DINERS - 1) % DINERS; // 右邻pthread_mutex_lock (&g_mtx);while (g_stats[l] == EATING || g_stats[r] == EATING) {  //只有左右两个人都不吃的时候自己才能吃g_stats[i] = HUNGRY;printf ("%s:快点吧,饿死我了~~~\n", g_names[i]);pthread_cond_wait (&g_conds[i], &g_mtx);      //等待的时候把锁放开}g_stats[i] = EATING;printf ("%s:终于可以吃一口了!\n", g_names[i]);pthread_mutex_unlock (&g_mtx);usleep ((rand () % 100) * 10000);  //吃的时间随机给出}// 思考void think (int i) {int l = (i + 1) % DINERS; // 左邻int r = (i + DINERS - 1) % DINERS; // 右舍pthread_mutex_lock (&g_mtx);g_stats[i] = THINKING;printf ("%s:吃饱了,开始思考...\n", g_names[i]);pthread_cond_signal (&g_conds[l]); // 令其重新获得原先的互斥锁pthread_cond_signal (&g_conds[r]);pthread_mutex_unlock (&g_mtx);usleep ((rand () % 100) * 10000);}// 就餐者线程void* diner (void* arg) {int i = (int)arg; //拿到哲学家的索引号for (;;) {eat (i);think (i);}return NULL;}int main (void) {srand (time (NULL));pthread_attr_t attr;int error = pthread_attr_init (&attr);if (error) {fprintf (stderr, "pthread_attr_init: %s\n", strerror (error));return -1;}if ((error = pthread_attr_setdetachstate (&attr,PTHREAD_CREATE_DETACHED)) != 0) {fprintf (stderr, "pthread_attr_setdetachstate: %s\n",strerror (error));return -1;}size_t i;pthread_t tid;for (i = 0; i < DINERS; ++i)if ((error = pthread_create (&tid, &attr, diner,(void*)i)) != 0) {fprintf (stderr, "pthread_create: %s\n", strerror (error));return -1;}getchar ();return 0;}


0 0
原创粉丝点击