Linux--執行緒安全-(下)
阿新 • • 發佈:2019-01-13
文章目錄
條件變數
需要一個條件:表示臨界區有沒有資源
為什麼條件變數要和互斥鎖搭配使用?
因為等待需要被喚醒,而被喚醒的前提條件就是條件已經滿足,並且這個條件本身就是一個臨界資源,因此改變條件的操作需要被保護。
條件變數的初始化及銷燬:
int pthread_cond_destroy(pthread_cond_t *cond);
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);
//解鎖+等待,當被喚醒時候,它自動獲得鎖
還有限時等待的函式:
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);
pthread_cond_wait函式會對互斥鎖做判斷,如果呼叫執行緒加鎖,就解鎖,然後陷入等待,整個過程是原子操作,防止消費者先拿到鎖,發現條件變數不滿足,無法消費,它陷入阻塞等待,這時候生產者得不到鎖。
喚醒在條件變數上的執行緒:
int pthread_cond_broadcast(pthread_cond_t *cond);//廣播喚醒
int pthread_cond_signal(pthread_cond_t *cond);//喚醒第一個等待的條件變數的執行緒
條件變數程式碼演示:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <errno.h>
pthread_cond_t cond;
pthread_mutex_t mutex;
int basket = 0;//公共條件,需要互斥鎖來保證原子性
void* saler(void*arg)
{
while(1){
pthread_mutex_lock(mutex);
if(basket == 0){
printf("I sell a good\n");
basket = 1;
pthread_cond_signal(&cond);
pthread_mutex_unlock(mutex);
}
}
return NULL;
}
void* customer(void*arg)
{
while(1){
pthread_mutex_lock(mutex);
if(basket == 0){
//初始狀態等待,睡眠
//pthread_cond_wait函式會對互斥鎖做判斷,如果呼叫執行緒加鎖,就解鎖,然後陷入等待,整個過程是原子操作
pthread_cond_wait(&cond,&mutex);
}
printf("I bought a gift for my girl friend!!\n");
basket = 0;
pthread_mutex_unlock(mutex);
}
return NULL;
}
int main()
{
pthread_t t1,t2;
int ret;
pthread_cond_init(&cond,NULL);//條件變數初始化
pthread_mutex_init(&mutex,NULL);
ret = pthread_create(&t1,NULL,saler,NULL);
if(ret != 0){
perror("pthread_create error");
exit(-1);
}
ret = pthread_create(&t1,NULL,customer,NULL);
if(ret != 0){
perror("pthread_create error");
exit(-1);
}
pthread_join(t1,NULL);
pthread_join(t2,NULL);
pthread_cond_destroy(&cond);
pthread_mutex_destroy(&mutex);
return 0;
}
Posix訊號量
POSIX訊號量和SystemV訊號量作用相同,都是用於同步操作,達到無衝突的訪問共享資源的目的。 但POSIX可以用於執行緒間同步。systemV標準posix實現程序間通訊
即可以實現同步也可以實現互斥,既可以用於程序間同步與互斥,也可以用於執行緒間的同步與互斥。
訊號量是什麼(具有一個等待佇列的計數器)
posix執行緒同步實現
消費者:沒有資源則等待
生產者:生產出來則通知佇列中的等待者
1、訊號量的初始化
int sem_init(sem_t *sem, int pshared, unsigned int value);
- If pshared has the value 0, then the semaphore is shared between the threads of a process
If pshared is nonzero, then the semaphore is shared between processes
sem:訊號量變數名
value:訊號量初始計數
成功:0 失敗:-1
2、訊號量的操作(等待/通知)
等待:對於消費者,沒有資源則等待。等待訊號量,會將訊號量的值減1。
int sem_wait(sem_t *sem);//阻塞等待
int sem_trywait(sem_t *sem);//沒有資源,報錯返回
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);//限時等待,超時報錯返回
釋出訊號量:生產者生產出資源,通知消費者。釋出訊號量,表示資源使⽤完畢,可以歸還資源了。將訊號量值加1。
int sem_post(sem_t *sem);
3、訊號量的釋放
int sem_destroy(sem_t *sem);
Link with -pthread.
/*訊號量實現執行緒同步與互斥
* 同步:
* 1、訊號量的初始化
* 2、訊號量的操作(等待/通知)
* 3、訊號量的釋放
*/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <pthread.h>
#include <semaphore.h>
sem_t sem;//定義訊號量
void*thr_producer(void*arg)
{
while(1){
sleep(1);
printf("I make a hot beef noodles!!\n");
sem_post(&sem);
}
return NULL;
}
void*thr_customer(void*arg)
{
while(1){
sem_wait(&sem);
printf("It is declicious!!\n");
}
return NULL;
}
int main()
{
pthread_t t1,t2;
int ret;
sem_init(&sem,0,0);//訊號量初始化
ret = pthread_create(&t1,NULL,thr_producer,NULL);
if(ret != 0 ){
perror("pthread_create error");
exit(-1);
}
ret = pthread_create(&t2,NULL,thr_customer,NULL);
if(ret != 0 ){
perror("pthread_create error");
exit(-1);
}
pthread_join(t1,NULL);
pthread_join(t2,NULL);
sem_destroy(&sem);
return 0;
}
posix執行緒互斥實現
訊號量的操作(等待+通知)
sem_wait()訊號量減1
sem_post()釋出訊號量,表示資源使⽤完畢,可以歸還資源了。將訊號量值加1。
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <pthread.h>
#include <semaphore.h>
sem_t sem;//定義訊號量
int ticket = 100;//黃牛搶票,總票數100
void* buy_ticket(void*arg)
{
int id = (int)arg;
while(1){
sem_wait(&sem);
if(ticket > 0){
usleep(1000);
ticket--;
printf("%d Buy a ticket,the ticket has %d\n",id,ticket);
}
sem_post(&sem);
}
return NULL;
}
int main()
{
pthread_t tid[4];
int ret;
sem_init(&sem,0,1);//訊號量初始化
int i = 0;
for(i=0;i<4;i++){
ret = pthread_create(&tid[i],NULL,buy_ticket,(void*)i);
if(ret != 0 ){
perror("pthread_create error");
exit(-1);
}
}
pthread_join(tid[0],NULL);
pthread_join(tid[1],NULL);
pthread_join(tid[2],NULL);
pthread_join(tid[3],NULL);
sem_destroy(&sem);
return 0;
}
訊號量與條件變數在保證同步時候的區別:訊號量是修改自己內部的資源計數,這個內部的資源計數就是條件,而條件變數修改的是外部的條件,需要我們使用者來修改
STL自身不能保證原子性
訊號量的操作是一個原子操作。