線程同步與互斥:
我們先來一個示例看一下:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <sys/syscall.h>
int i = ;
void* fun(void* arg)
{
char* id = (char *)arg;
while()
{
if(i > )
{
sleep();
printf("%s say i=%d\n",id,i);
i--;
}
else
{
break;
}
}
return NULL;
}
int main()
{
int i = ;
pthread_t tid1,tid2,tid3,tid4;
pthread_create(&tid1,NULL,fun,"1");
pthread_create(&tid2,NULL,fun,"2");
pthread_create(&tid3,NULL,fun,"3");
pthread_create(&tid4,NULL,fun,"4");
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
pthread_join(tid3,NULL);
pthread_join(tid4,NULL);
return ;
}
按照我們了解的這個函數應該會輸出:i從10—1的順序。那麼結果呢?
怎麼會出現呢-1,-2呢?
這就是這個程式會出錯的表面原因。那根本原因是什麼呢?
這裡就涉及到了原子性的概念,我們來看一下在cpu的角度下對待–操作是什麼樣的?
擴充一個指令,檢視目前程式的彙編代碼:
[test@localhost lianxi]$ objdump -d test > test.objdump
400724: 8b 05 22 09 20 00 mov 0x200922(%rip),%eax # 60104c <i>
40072a: 83 e8 01 sub $0x1,%eax
40072d: 89 05 19 09 20 00 mov %eax,0x200919(%rip) # 60104c <i>
這是函數執行的彙編代碼中“ - - ”操作。我們可以看到–操作在cpu中執行了3步,第一步将i的值移動到寄存器中,第二步cpu進行-1操作,第三步将操作完成的值寫回到i的位址處。
這個中間的過程中如果進行了線程切換,那将會産生錯誤。
歸根結底還是–操作不是原子性操作。
想解決這個問題,很簡單就是建立一個鎖。每次對一個值進行操作的前提必須擁有鎖。而我們知道線程中是有共享的記憶體的,是以我們就在共享記憶體的地方建立一個鎖,使每個線程都可以看到這把鎖。Linux中稱這把鎖為“互斥量”。
互斥量:
一、互斥鎖
互斥鎖,是一種信号量,常用來防止兩個程序或線程在同一時刻通路相同的共享資源。
需要的頭檔案:pthread.h
互斥鎖辨別符:pthread_mutex_t
()互斥鎖初始化:
函數原型: int pthread_mutex_init (pthread_mutex_t* mutex,const pthread_mutexattr_t* mutexattr);
函數傳入值: mutex:互斥鎖。
mutexattr:PTHREAD_MUTEX_INITIALIZER 建立快速互斥鎖。
PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP 建立遞歸互斥鎖。
PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP 建立檢錯互斥鎖。
函數傳回值:成功:;出錯:-
()互斥操作函數
int pthread_mutex_lock(pthread_mutex_t* mutex); //上鎖
int pthread_mutex_trylock (pthread_mutex_t* mutex); //隻有在互斥被鎖住的情況下才阻塞
int pthread_mutex_unlock (pthread_mutex_t* mutex); //解鎖
int pthread_mutex_destroy (pthread_mutex_t* mutex); //清除互斥鎖
函數傳入值:mutex:互斥鎖。
函數傳回值:成功:;出錯:-
我們改進一下之前的代碼:
int i = ;
pthread_mutex_t mutex;
void* fun(void* arg)
{
char* id = (char *)arg;
while()
{
pthread_mutex_lock(&mutex);
if(i > )
{
printf("%s say i=%d\n",id,i);
i--;
pthread_mutex_unlock(&mutex);
}
else
{
pthread_mutex_unlock(&mutex);
break;
}
sleep();
}
return NULL;
}
int main()
{
int i = ;
pthread_t tid1,tid2,tid3,tid4;
pthread_create(&tid1,NULL,fun,"1");
pthread_create(&tid2,NULL,fun,"2");
pthread_create(&tid3,NULL,fun,"3");
pthread_create(&tid4,NULL,fun,"4");
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
pthread_join(tid3,NULL);
pthread_join(tid4,NULL);
return ;
}
效果如下:
這樣效果就出來了吧!
線程同步:(條件變量)
- 當線程通路互斥的某個變量時,它在其他線程沒有釋放“鎖”之前什麼也幹不了。
- 例如動物園裡飼養員給動物為食,在飼養員正在放食物的時候,動物被關在籠子了,什麼也幹不了。隻有當飼養員把食物放好後,打開籠子,動物才能吃東西。
有關函數:
int pthread_cond_destroy(pthread_cond_t *cond);
//删除條件變量
int pthread_cond_init(pthread_cond_t *restrict cond,
const pthread_condattr_t *restrict attr);
//初始化
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
//建立
int pthread_cond_wait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex);
//等待條件滿足(動物在籠子等待飼養員喂食物的過程)
int pthread_cond_broadcast(pthread_cond_t *cond);
//喚醒多個線程(飼養員放好了食物,打開了許多動物的籠子)
int pthread_cond_signal(pthread_cond_t *cond);
//喚醒單個線程(飼養員放好了食物,隻打開了一個動物的籠子)
示例:
pthread_cond_t cond;
pthread_mutex_t mutex;
void* fun()
{
while()
{
pthread_cond_wait(&cond,&mutex);
printf("tager\n");
}
}
void* fun2()
{
while()
{
pthread_cond_signal(&cond);
sleep();
}
}
int main()
{
int i = ;
pthread_cond_init(&cond,NULL);
pthread_mutex_init(&mutex,NULL);
pthread_t tid1,tid2;
pthread_create(&tid1,NULL,fun,NULL);
pthread_create(&tid2,NULL,fun2,NULL);
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
pthread_cond_destroy(&cond);
return ;
}
效果執行個體:
在這個pthread_cond_wait函數中為什麼第二個參數要是一個互斥鎖呢?
條件等待是線程通信間的一種必要的方式,如果一個線程所需要的資源不滿足,他就會一直等待,是以必須要有另一個線程來改變它,這時就是倆個線程共享一塊資源了,是以必須要加上鎖。