http://zhanglibin.blog.51cto.com/2553477/610354
From:《POSIX多线程编程》
生成方和使用者问题
并发编程中收集了许多标准的众所周知的问题,生成方和使用者问题只是其中的一个问题。此问题涉及到一个大小限定的缓冲区和两类线程(生成方和使用者),生成方将项放入缓冲区中,然后使用者从缓冲区中取走项。
生成方必须在缓冲区中有可用空间之后才能向其中放置内容。使用者必须在生成方向缓冲区中写入之后才能从中提取内容。
条件变量表示一个等待某个条件获得信号的线程队列。示例4-11 中包含两个此类队列。一个队列(less)针对生成方,用于等待缓冲区中出现空位置。另一个队列(more)针对使用者,用于等待从缓冲槽位的空位置中提取其中包含的信息。该示例中还包含一个互斥锁,因为描述该缓冲区的数据结构一次只能由一个线程访问。
示例4-11生成方和使用者的条件变量问题
typedef struct
{
char buf[BSIZE];
int occupied;
int nextin;
int nextout;
pthread_mutex_t mutex;
pthread_cond_t more;
pthread_cond_t less;
} buffer_t;
buffer_t buffer;
如示例4-12中所示,生成方线程获取该互斥锁以保护buffer数据结构,然后,缓冲区确定是否有空间可用于存放所生成的项。如果没有可用空间,生成方线程会调用pthread_cond_wait()。pthread_cond_wait()会导致生成方线程连接正在等待less条件获得信号的线程队列。less表示缓冲区中的可用空间。
于此同时,在调用pthread_cond_wait()的过程中,该线程会释放互斥锁的锁定。正在等待的生成方线程依赖于使用者线程在条件为真时发出信号,如示例4-12中所示。该条件获得信号时,将会唤醒等待less的第一个线程。但是,该线程必须再次锁定互斥锁,然后才能从pthread_cond_wait()返回。
获取互斥锁可确保该线程再次以独占方式访问缓冲区的数据结构。该线程随后必须检查缓冲区中是否确实存在可用空间。如果空间可用,该线程会向下一个可用的空位置中进行写入。
与此同时,使用者线程可能正在等待项出现在缓冲区中。这些线程正在等待条件变量more。刚在缓冲区中存储内容的生成方线程会调用pthread_cond_signal()
以唤醒下一个正在等待的使用者。如果没有正在等待的使用者,此调用将不起作用。
最后,生成方线程会解除锁定互斥锁,从而允许其他线程处理缓冲区的数据结构。
示例4-12生成方和使用者问题:生成方
void producer(buffer_t * b, char item)
{
pthread_mutex_lock(&b->mutex);
while(b->occupied == BSIZE)
pthread_cond_wait(&b->less, &b->mutex);
b->buf[b->nextin++] = item;
b->nextin %= BSIZE;
b->occupied++;
pthread_cond_signal(&b->more);
pthread_mutex_unlock(&b->mutex);
}
示例4-13生成方和使用者问题:使用者
char consumer(buffer_t * b)
{
char item;
pthread_mutex_lock(&b->mutex);
while(b->occupied <= 0)
pthread_cond_wait(&b->more, &b->mutex);
item = b->buf[b->nextout++];
b->nextout %= BSIZE;
b->occupied--;
pthread_cond_signal(&b->less);
pthread_mutex_unlock(&b->mutex);
return (item);
}
2011年11月4日
在转载这篇文章几个月后,一个朋友的项目中需要使用这个模式。并且,我进行的一个AIX项目,也需要这个模式。我将其封装,形成了以下两个文件。
List.h
- /**
- * @file List.h
- * @author George Smith Patton
- * 张立斌
- */
- #ifndef BIN_LIST_H
- #define BIN_LIST_H
- #include <pthread.h>
- #define CAST(Cls, obj) ((Cls)(obj))
- #define OFFSET_OF(Cls, member) \
- CAST(unsigned long, &(CAST(Cls *, 0)->member))
- #define CONTAINER_OF(pObj, Cls, member) \
- CAST(Cls *, CAST(unsigned long, pObj)-OFFSET_OF(Cls, member))
- #ifdef __cplusplus
- #define STATIC_INLINE inline
- #else
- #define STATIC_INLINE static inline
- #endif
- typedef struct ListNode
- {
- struct ListNode * prev;
- struct ListNode * next;
- } ListNode, List;
- #define LIST_INIT(list) {&(list), &(list)}
- STATIC_INLINE void ListInit(List * const list)
- {
- list->prev = list;
- list->next = list;
- }
- STATIC_INLINE void __ListAdd(ListNode * const prev, ListNode * const next,
- ListNode * const node)
- {
- node->prev = prev;
- node->next = next;
- prev->next = node;
- next->prev = node;
- }
- STATIC_INLINE void ListAddHead(List * const list,
- ListNode * const node)
- {
- __ListAdd(list, list->next, node);
- }
- STATIC_INLINE void ListAddTail(List * const list,
- ListNode * const node)
- {
- __ListAdd(list->prev, list, node);
- }
- STATIC_INLINE void __ListDel(ListNode * const prev, ListNode * const next)
- {
- prev->next = next;
- next->prev = prev;
- }
- STATIC_INLINE ListNode * ListDelNode(const ListNode * const node)
- {
- __ListDel(node->prev, node->next);
- return CAST(ListNode *, node);
- }
- STATIC_INLINE ListNode * ListDelHead(const List * const list)
- {
- return ListDelNode(list->next);
- }
- STATIC_INLINE ListNode * ListDelTail(const List * const list)
- {
- return ListDelNode(list->prev);
- }
- typedef struct
- {
- volatile List list;
- pthread_mutex_t mutex;
- pthread_cond_t condEmpty;
- pthread_cond_t condFull;
- volatile unsigned size;
- unsigned maxSize;
- volatile unsigned producerSize;
- } ProducerConsumerList;
- #define PRODUCER_CONSUMER_INIT(pcList, maxSz, producerSz) \
- {LIST_INIT(*CAST(List *, &(pcList).list)), PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, PTHREAD_COND_INITIALIZER, \
- 0, (maxSz), producerSz}
- #ifdef __cplusplus
- extern "C" {
- #endif
- int ProducerConsumerListInit(ProducerConsumerList * const list, const unsigned maxSize, const unsigned producerSize);
- void ProducerConsumerListAddHead(ProducerConsumerList * const list, volatile ListNode * const node);
- void ProducerConsumerListAddTail(ProducerConsumerList * const list, volatile ListNode * const node);
- ListNode * ProducerConsumerListDelHead(ProducerConsumerList * const list);
- ListNode * ProducerConsumerListDelTail(ProducerConsumerList * const list);
- void ProducerConsumerListOneProducerEnd(ProducerConsumerList * const list);
- void ProducerConsumerListDel(ProducerConsumerList * const list);
- #ifdef __cplusplus
- }
- #endif
- #endif
List.c
- /**
- * @file List.c
- * @author George Smith Patton
- * 张立斌
- */
- #include "List.h"
- int ProducerConsumerListInit(ProducerConsumerList * const list, const unsigned maxSize, const unsigned producerSize)
- {
- int result;
- ListInit(CAST(List *, &list->list));
- result = pthread_mutex_init(&list->mutex, 0);
- if(result)
- return result;
- result = pthread_cond_init(&list->condEmpty, 0);
- if(result)
- return result;
- result = pthread_cond_init(&list->condFull, 0);
- if(result)
- return result;
- list->size = 0;
- list->maxSize = maxSize;
- list->producerSize = producerSize;
- return result;
- }
- void ProducerConsumerListAddHead(ProducerConsumerList * const list, volatile ListNode * const node)
- {
- pthread_mutex_lock(&list->mutex);
- while(list->size == list->maxSize)
- pthread_cond_wait(&list->condFull, &list->mutex);
- __ListAdd(CAST(ListNode *, &list->list), list->list.next, CAST(ListNode *, node));
- ++list->size;
- pthread_cond_signal(&list->condEmpty);
- pthread_mutex_unlock(&list->mutex);
- }
- void ProducerConsumerListAddTail(ProducerConsumerList * const list, volatile ListNode * const node)
- {
- pthread_mutex_lock(&list->mutex);
- while(list->size == list->maxSize)
- pthread_cond_wait(&list->condFull, &list->mutex);
- __ListAdd(list->list.prev, CAST(ListNode *, &list->list), CAST(ListNode *, node));
- ++list->size;
- pthread_cond_signal(&list->condEmpty);
- pthread_mutex_unlock(&list->mutex);
- }
- ListNode * ProducerConsumerListDelHead(ProducerConsumerList * const list)
- {
- pthread_mutex_lock(&list->mutex);
- while(0 == list->size)
- {
- if(0 == list->producerSize)
- {
- pthread_mutex_unlock(&list->mutex);
- return 0;
- }
- else
- pthread_cond_wait(&list->condEmpty, &list->mutex);
- }
- ListNode * result = ListDelNode(list->list.next);
- --list->size;
- pthread_cond_signal(&list->condFull);
- pthread_mutex_unlock(&list->mutex);
- return result;
- }
- ListNode * ProducerConsumerListDelTail(ProducerConsumerList * const list)
- {
- pthread_mutex_lock(&list->mutex);
- while(0 == list->size)
- {
- if(0 == list->producerSize)
- {
- pthread_mutex_unlock(&list->mutex);
- return 0;
- }
- else
- pthread_cond_wait(&list->condEmpty, &list->mutex);
- }
- ListNode * result = ListDelNode(list->list.prev);
- --list->size;
- pthread_cond_signal(&list->condFull);
- pthread_mutex_unlock(&list->mutex);
- return result;
- }
- void ProducerConsumerListOneProducerEnd(ProducerConsumerList * const list)
- {
- pthread_mutex_lock(&list->mutex);
- --list->producerSize;
- pthread_cond_signal(&list->condEmpty);
- pthread_mutex_unlock(&list->mutex);
- }
- void ProducerConsumerListDel(ProducerConsumerList * const list)
- {
- pthread_mutex_destroy(&list->mutex);
- pthread_cond_destroy(&list->condEmpty);
- pthread_cond_destroy(&list->condFull);
- }
并进行了简单的测试:
threadTest.c
- /**
- * @file threadTest.c
- * @author George Smith Patton
- * 张立斌
- */
- #include "List.h"
- #include <unistd.h>
- #include <stdio.h>
- #include <stdlib.h>
- typedef struct
- {
- ListNode node;
- int a;
- } AListNode;
- static inline AListNode * AListNodeMalloc(int a)
- {
- AListNode * result;
- for(;;)
- {
- result = CAST(AListNode *, malloc(sizeof(AListNode)));
- if(result)
- break;
- perror(__func__);
- sleep(1);
- }
- result->a = a;
- printf("%s %lX %d\n", __func__, CAST(unsigned long, result), result->a);
- return result;
- }
- static inline void AListNodeFree(const AListNode * const a)
- {
- printf("%s %lX %d\n", __func__, CAST(unsigned long, a), a->a);
- free(CAST(AListNode *, a));
- }
- typedef struct
- {
- ProducerConsumerList * list;
- } ThreadArg;
- void * thread1(void * arg);
- void * thread2(void * arg);
- void * thread1(void * arg)
- {
- ThreadArg * threadArg = CAST(ThreadArg *, arg);
- int index;
- int arr[] = {2, 3, 5, 7, 11, 13, 19, 23};
- for(index=0; index<8; ++index)
- {
- AListNode * a = AListNodeMalloc(arr[index]);
- ProducerConsumerListAddTail(threadArg->list, &a->node);
- }
- ProducerConsumerListOneProducerEnd(threadArg->list);
- return 0;
- }
- void * thread2(void * arg)
- {
- ThreadArg * threadArg = CAST(ThreadArg *, arg);
- ListNode * node;
- while(0 != (node = ProducerConsumerListDelHead(threadArg->list)))
- {
- AListNode * a = CONTAINER_OF(node, AListNode, node);
- AListNodeFree(a);
- }
- return 0;
- }
- int main(void)
- {
- int result;
- ProducerConsumerList list = PRODUCER_CONSUMER_INIT(list, -1, 1);
- ThreadArg threadArg = {&list};
- pthread_t t1Id = 0;
- pthread_t t2Id = 0;
- result = pthread_create(&t1Id, 0, thread1, &threadArg);
- if(result)
- goto End;
- result = pthread_create(&t2Id, 0, thread2, &threadArg);
- if(result)
- goto End;
- if(t1Id)
- {
- result = pthread_join(t1Id, 0);
- if(result)
- goto End;
- }
- if(t2Id)
- {
- result = pthread_join(t2Id, 0);
- if(result)
- goto End;
- }
- ProducerConsumerListDel(&list);
- End:
- return result;
- }