本文目錄一覽:
- 1、在linux下用c語言實現用多進程同步方法演示“生產者-消費者”問題
- 2、如何使用pthread
- 3、使用條件變量進行線程間的同步
- 4、Linux C語言pthread_mutex_unlock和pthread_cond_signal順序問題
- 5、C語言如何終止線程?
- 6、C語言多線程的操作步驟
在linux下用c語言實現用多進程同步方法演示“生產者-消費者”問題
這個問題需要的知識主要包括:
1 多進程間進行通信;
2 使用同步信號量(semaphore)和互斥信號量(mutex)進行數據保護。
參考代碼如下,可以參照注釋輔助理解:
#include stdio.h
#include stdlib.h
#include unistd.h
#include pthread.h
#include semaphore.h
#define N 2 // 消費者或者生產者的數目
#define M 10 // 緩衝數目
int in = 0; // 生產者放置產品的位置
int out = 0; // 消費者取產品的位置
int buff[M] = {0}; // 緩衝初始化為0, 開始時沒有產品
sem_t empty_sem; // 同步信號量, 當滿了時阻止生產者放產品
sem_t full_sem; // 同步信號量, 當沒產品時阻止消費者消費
pthread_mutex_t mutex; // 互斥信號量, 一次只有一個線程訪問緩衝
int product_id = 0; //生產者id
int prochase_id = 0; //消費者id
/* 打印緩衝情況 */
void print()
{
int i;
for(i = 0; i M; i++)
printf(“%d “, buff[i]);
printf(“\n”);
}
/* 生產者方法 */
void *product()
{
int id = ++product_id;
while(1)
{
// 用sleep的數量可以調節生產和消費的速度,便於觀察
sleep(1);
//sleep(1);
sem_wait(empty_sem);
pthread_mutex_lock(mutex);
in = in % M;
printf(“product%d in %d. like: \t”, id, in);
buff[in] = 1;
print();
++in;
pthread_mutex_unlock(mutex);
sem_post(full_sem);
}
}
/* 消費者方法 */
void *prochase()
{
int id = ++prochase_id;
while(1)
{
// 用sleep的數量可以調節生產和消費的速度,便於觀察
sleep(1);
//sleep(1);
sem_wait(full_sem);
pthread_mutex_lock(mutex);
out = out % M;
printf(“prochase%d in %d. like: \t”, id, out);
buff[out] = 0;
print();
++out;
pthread_mutex_unlock(mutex);
sem_post(empty_sem);
}
}
int main()
{
pthread_t id1[N];
pthread_t id2[N];
int i;
int ret[N];
// 初始化同步信號量
int ini1 = sem_init(empty_sem, 0, M);
int ini2 = sem_init(full_sem, 0, 0);
if(ini1 ini2 != 0)
{
printf(“sem init failed \n”);
exit(1);
}
//初始化互斥信號量
int ini3 = pthread_mutex_init(mutex, NULL);
if(ini3 != 0)
{
printf(“mutex init failed \n”);
exit(1);
}
// 創建N個生產者線程
for(i = 0; i N; i++)
{
ret[i] = pthread_create(id1[i], NULL, product, (void *)(i));
if(ret[i] != 0)
{
printf(“product%d creation failed \n”, i);
exit(1);
}
}
//創建N個消費者線程
for(i = 0; i N; i++)
{
ret[i] = pthread_create(id2[i], NULL, prochase, NULL);
if(ret[i] != 0)
{
printf(“prochase%d creation failed \n”, i);
exit(1);
}
}
//銷毀線程
for(i = 0; i N; i++)
{
pthread_join(id1[i],NULL);
pthread_join(id2[i],NULL);
}
exit(0);
}
在Linux下編譯的時候,要在編譯命令中加入選項-lpthread以包含多線程支持。比如存儲的C文件為demo.c,要生成的可執行文件為demo。可以使用命令:
gcc demo.c -o demo -lpthread
程序中為便於觀察,使用了sleep(1);來暫停運行,所以查看輸出的時候可以看到,輸出是每秒打印一次的。
如何使用pthread
怎樣知道一個pthread
如果需要只終止某個線程而不終止整個進程,可以有三種方法:
1.從線程函數return。這種方法對主線程不適用,從main函數return相當於調用exit。
2.一個線程可以調用pthread_cancel終止同一進程中的另一個線程。
3.線程可以調用pthread_exit終止自己。
使用條件變量進行線程間的同步
先看一下APUE第三版對於條件變量的說明:
條件變量是另一種線程同步機制,它為線程間共享的對象提供了同步的方法。當條件變量配合互斥鎖(Mutex)使用時,允許多個線程處在一種自由等待任意條件發生的狀態。
條件變量自身由互斥鎖(Mutex)保護。線程必須在修改條件狀態之前先對其上鎖,其他線程不會在獲取鎖之前被通知到其狀態變化,因為只有獲取到鎖才可以計算條件。
條件變量的數據類型是 pthread_cond_t ,條件變量屬性的類型是 pthread_condattr_t ,它們都包含在頭文件pthread.h中。
條件變量使用之前必須初始化,有兩種方法:
需要釋放條件變量時,使用pthread_cond_destroy即可。
調用phread_cond_wait或pthread_cond_timewait(以下簡稱wait函數)可以使當前線程等待某一條件的發生。兩者的區別在於後者可以指定等待時間。
調用wait函數時,系統使調用線程進入等待狀態後 釋放鎖 (所以我們必須先加鎖後調用wait函數)。在這一步操作中的檢查條件和進入等待是 原子操作 ,所以線程不會錯過條件的變化。當wait函數返回時,mutex會 再次被加鎖 。
其中pthread_cond_timewait中用到的timespec結構定義如下:
需要注意的是,timespec是一個絕對時間,所以在使用前我們需要先取得當前時間,再加上等待時間。例如下面這樣:
如果時間到了還沒有等到條件變化,函數會對mutex重新加鎖並返回一個ETIMEOUT的錯誤。
當wait函數返回成功時, 需要重新檢查條件 ,因為條件有可能已經被其他線程修改。
當條件滿足時,可以用這兩個函數用來通知其他線程。
pthread_cond_signal會喚醒至少一個等待的線程,而pthread_cond_broadcast會喚醒所有等待的線程。必須注意的是:我們 必須在狀態發生變化之後再發送信號給其他線程 。
條件變量的數據類型是 pthread_cond_t ,它主要有兩種屬性:
設置進程間共享屬性:
設置時鐘屬性:
pthread_cond_timewait函數用於在等待條件變量時提供超時功能,不過該函數的超時時間是一個絕對時間。默認使用系統時間,這意味着若修改系統時間,那麼超時就不準確:有可能提前返回,也可能要幾年才返回。這在某些情況下會導致bug,這時我們可以通過設置條件變量的時鐘屬性來避免這個問題。下面的例子展示了如何使用這個屬性:
Linux C語言pthread_mutex_unlock和pthread_cond_signal順序問題
signal前解鎖是錯誤,順序相當重要,鎖才能保證你整個操作是完全原子,signal只是整個操作的一部分,它不能被分割出去。用特殊一點的情況來解釋:如果解鎖後才signal,那麼有可能信號一直發不出去,因為信號線程一直得到不調度。
C語言如何終止線程?
面只有兩個線程,是生產者/消費者模式,已編譯通過,注釋很詳細。
/* 以生產者和消費者模型問題來闡述Linux線程的控制和通信你
生產者線程將生產的產品送入緩衝區,消費者線程則從中取出產品。
緩衝區有N個,是一個環形的緩衝池。
*/
#include stdio.h
#include pthread.h
#define BUFFER_SIZE 16
struct prodcons
{
int buffer[BUFFER_SIZE];/*實際存放數據的數組*/
pthread_mutex_t lock;/*互斥體lock,用於對緩衝區的互斥操作*/
int readpos,writepos; /*讀寫指針*/
pthread_cond_t notempty;/*緩衝區非空的條件變量*/
pthread_cond_t notfull;/*緩衝區未滿 的條件變量*/
};
/*初始化緩衝區*/
void pthread_init( struct prodcons *p)
{
pthread_mutex_init(p-lock,NULL);
pthread_cond_init(p-notempty,NULL);
pthread_cond_init(p-notfull,NULL);
p-readpos = 0;
p-writepos = 0;
}
/*將產品放入緩衝區,這裡是存入一個整數*/
void put(struct prodcons *p,int data)
{
pthread_mutex_lock(p-lock);
/*等待緩衝區未滿*/
if((p-writepos +1)%BUFFER_SIZE ==p-readpos)
{
pthread_cond_wait(p-notfull,p-lock);
}
p-buffer[p-writepos] =data;
p-writepos++;
if(p-writepos = BUFFER_SIZE)
p-writepos = 0;
pthread_cond_signal(p-notempty);
pthread_mutex_unlock(p-lock);
}
/*從緩衝區取出整數*/
int get(struct prodcons *p)
{
int data;
pthread_mutex_lock(p-lock);
/*等待緩衝區非空*/
if(p-writepos == p-readpos)
{
pthread_cond_wait(p-notempty ,p-lock);//非空就設置條件變量notempty
}
/*讀書據,移動讀指針*/
data = p-buffer[p-readpos];
p-readpos++;
if(p-readpos == BUFFER_SIZE)
p-readpos = 0;
/*設置緩衝區未滿的條件變量*/
pthread_cond_signal(p-notfull);
pthread_mutex_unlock(p-lock);
return data;
}
/*測試:生產站線程將1 到1000的整數送入緩衝區,消費者線程從緩衝區中獲取整數,兩者都打印信息*/
#define OVER (-1)
struct prodcons buffer;
void *producer(void *data)
{
int n;
for( n=0;n1000;n++)
{
printf(“%d ——\n”,n);
put(buffer,n);
}
put(buffer,OVER);
return NULL;
}
void *consumer(void *data)
{
int d;
while(1)
{
d = get(buffer);
if(d == OVER)
break;
else
printf(“—–%d\n”,d);
}
return NULL;
}
int main()
{
pthread_t th_p,th_c;
void *retval;
pthread_init(buffer);
pthread_create(th_p,NULL,producer,0);
pthread_create(th_c,NULL,consumer,0);
/*等待兩個線程結束*/
pthread_join(th_p, retval);
pthread_join(th_c,retval);
return 0;
}
C語言多線程的操作步驟
線程創建
函數原型:intpthread_create(pthread_t*restrict tidp,const pthread_attr_t *restrict attr,void *(*start_rtn)(void),void *restrict arg);
返回值:若是成功建立線程返回0,否則返回錯誤的編號。
形式參數:pthread_t*restrict tidp要創建的線程的線程id指針;const pthread_attr_t *restrict attr創建線程時的線程屬性;void *(start_rtn)(void)返回值是void類型的指針函數;void *restrict arg start_rtn的形參。
線程掛起:該函數的作用使得當前線程掛起,等待另一個線程返回才繼續執行。也就是說當程序運行到這個地方時,程序會先停止,然後等線程id為thread的這個線程返回,然後程序才會斷續執行。
函數原型:intpthread_join(pthread_tthread, void **value_ptr);
參數說明如下:thread等待退出線程的線程號;value_ptr退出線程的返回值。
返回值:若成功,則返回0;若失敗,則返回錯誤號。
線程退出
函數原型:voidpthread_exit(void *rval_ptr);
獲取當前線程id
函數原型:pthread_tpthread_self(void);
互斥鎖
創建pthread_mutex_init;銷毀pthread_mutex_destroy;加鎖pthread_mutex_lock;解鎖pthread_mutex_unlock。
條件鎖
創建pthread_cond_init;銷毀pthread_cond_destroy;觸發pthread_cond_signal;廣播pthread_cond_broadcast;等待pthread_cond_wait。
原創文章,作者:AGRF,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/141295.html