以下爲看視頻筆記..........
1.生產者消費者問題模型
生產者消費者問題是一個經典的、多線程同步問題。
有兩個線程: - -個生產者線程和一個消費者線程。兩個線程共享-個初始爲空、固定大小爲n的緩存區。.
生產者的工作是“生產”-段數據,只有緩衝區沒滿時,生產者才能把消息放入到緩衝區,否則必須等待,如此反覆;
同時,只有緩衝區非空時,消費者才能從中取出數據,- 次消費- -段數據, 否則必須等待,如此反覆。
問題的核心是:
1.1 要保證不讓生產者在緩存還是滿的時候仍然要向內寫數據;
1.2 不讓消費者試圖從空的緩存中取出數據。
2. 生產者消費者問題本質
解決生產者消費者問題實際上是要解決線程間互斥關係問題(指的是共享資源)同步關係問題(線程的步調)
由於緩衝區是臨界資源,它一個時刻只允許-一個生產者放入消息,或者--個消費者從中取出消息,所以這裏我們需要解決一個互斥訪問的問題。
同時生產者和消費者又是一- 個相互協作的關係,只有生產者生產之後,消費者才能消費,所以我們還需要解決一個同步的問題。
二值信號量指的是value值爲0或1,實現共享資源的互斥。首先把value值初始化爲1, 生產者線程訪問共享資源時,他先獲取二值信號量的值,因爲value初始化爲1,生產者可以操作共享資源;當這時候消費者線程去獲取共享資源時,此時value值被生產者賦值爲0了,所以消費者是獲取不到共享資源的,達到共享資源保護的作用。
3. 實例代碼
在produer_consumer.c中
/*
* 程序清單:生產者消費者例子
*
* 這個例子中將創建兩個線程用於實現生產者消費者問題
*(1)生產者線程將cnt值每次加1並循環存入array數組的5個成員內;
*(2)消費者線程將生產者中生產的數值打印出來,並累加求和
*/
#include <rtthread.h>
#define THREAD_PRIORITY 6
#define THREAD_STACK_SIZE 512
#define THREAD_TIMESLICE 5
/* 定義最大5個元素能夠被產生 */
#define MAXSEM 5
/* 用於放置生產的整數數組 */
rt_uint32_t array[MAXSEM]; //數組爲共享資源
/* 指向生產者、消費者在array數組中的讀寫位置 */
static rt_uint32_t set, get;
/* 指向線程控制塊的指針 */
static rt_thread_t producer_tid = RT_NULL;
static rt_thread_t consumer_tid = RT_NULL;
struct rt_semaphore sem_lock;
struct rt_semaphore sem_empty, sem_full;
/* 生產者線程入口 */
void producer_thread_entry(void *parameter)
{
int cnt = 0;
/* 運行10次 */
while (cnt < 10)
{
/* 獲取一個空位 */
rt_sem_take(&sem_empty, RT_WAITING_FOREVER); //sem_empty的value值爲5
/* 修改array內容,上鎖 */
rt_sem_take(&sem_lock, RT_WAITING_FOREVER);
array[set % MAXSEM] = cnt + 1;
rt_kprintf("the producer generates a number: %d\n", array[set % MAXSEM]);
set++;
rt_sem_release(&sem_lock);
/* 發佈一個滿位 */
rt_sem_release(&sem_full);
cnt++;
/* 暫停一段時間 */
rt_thread_mdelay(20);
}
rt_kprintf("the producer exit!\n");
}
/* 消費者線程入口 */
void consumer_thread_entry(void *parameter)
{
rt_uint32_t sum = 0;
while (1)
{
/* 獲取一個滿位 */
rt_sem_take(&sem_full, RT_WAITING_FOREVER);//sem_full的初始值爲0的
/* 臨界區,上鎖進行操作 */
rt_sem_take(&sem_lock, RT_WAITING_FOREVER);
sum += array[get % MAXSEM];
rt_kprintf("the consumer[%d] get a number: %d\n", (get % MAXSEM), array[get % MAXSEM]);
get++;
rt_sem_release(&sem_lock);
/* 釋放一個空位 */
rt_sem_release(&sem_empty);
/* 生產者生產到10個數目,停止,消費者線程相應停止 */
if (get == 10) break;
/* 暫停一小會時間 */
rt_thread_mdelay(50);
}
rt_kprintf("the consumer sum is: %d\n", sum);
rt_kprintf("the consumer exit!\n");
}
int producer_consumer(void)
{
set = 0;
get = 0;
/* 初始化3個信號量 */
rt_sem_init(&sem_lock, "lock", 1, RT_IPC_FLAG_FIFO);
rt_sem_init(&sem_empty, "empty", MAXSEM, RT_IPC_FLAG_FIFO);
rt_sem_init(&sem_full, "full", 0, RT_IPC_FLAG_FIFO);
/* 創建生產者線程 */
producer_tid = rt_thread_create("producer",
producer_thread_entry, RT_NULL,
THREAD_STACK_SIZE,
THREAD_PRIORITY - 1, THREAD_TIMESLICE);//消費者線程優先級高於消費者,先生產在消費
if (producer_tid != RT_NULL)
{
rt_thread_startup(producer_tid);
}
else
{
rt_kprintf("create thread producer failed");
return -1;
}
/* 創建消費者線程 */
consumer_tid = rt_thread_create("consumer",
consumer_thread_entry, RT_NULL,
THREAD_STACK_SIZE,
THREAD_PRIORITY + 1, THREAD_TIMESLICE);
if (consumer_tid != RT_NULL)
{
rt_thread_startup(consumer_tid);
}
else
{
rt_kprintf("create thread consumer failed");
return -1;
}
return 0;
}
/* 導出到 msh 命令列表中 */
MSH_CMD_EXPORT(producer_consumer, producer_consumer sample);