前言
一、線程同步
在多線程環(huán)境下,多個線程可以并發(fā)地執(zhí)行,訪問共享資源(如內(nèi)存變量、文件、網(wǎng)絡(luò)連接 等)。
這可能導致 數(shù)據(jù)不一致性, 死鎖, 競爭條件等 問題。
為了解決這些問題,需要使用同步機制來確保線程間的協(xié)作和互斥訪問共享資源。
“同步” 的目的 是為了避免數(shù)據(jù)的混亂,解決與時間有關(guān)的錯誤。實際上,不僅線程需要同步,進程間,信號間等等都需要同步機制。
線程同步,指一個線程發(fā)出某一功能調(diào)用時,在沒有得到結(jié)果之前,該調(diào)用不返回。同時 其他線程為保證數(shù)據(jù)的一致性,不能調(diào)用該功能。
二、互斥量 mutex
互斥鎖(Mutex,全稱為 Mutual Exclusion)是一種常用的同步機制,用于保護共享資源免受多個線程同時訪問和修改的影響?;コ怄i提供了一種互斥訪問的機制,同一時間只允許一個線程獲取鎖并訪問被保護的資源。
每個線程在對資源操作前都嘗試進行先加鎖,成功加鎖才能操作,操作結(jié)束解鎖。
資源還是共享的,線程也還是競爭的。
但 通過 “鎖” 就將資源的訪問變成互斥操作,而后與時間有關(guān)的錯誤也就不會再產(chǎn)生了。
1. 互斥鎖的基本操作包括兩個關(guān)鍵操作:
-
加鎖(Lock):線程通過申請互斥鎖來獲取對共享資源的訪問權(quán)。如果互斥鎖當前未被其他線程獲取,線程成功獲得鎖然后進入臨界區(qū)(Critical Section),可以訪問共享資源。如果互斥鎖已經(jīng)被其他線程獲取,申請鎖的線程將被阻塞,直到鎖被釋放。
-
解鎖(Unlock):線程在完成對共享資源的訪問之后,釋放互斥鎖,使得其他線程可以申請并獲取鎖。
2. 互斥鎖的主要應(yīng)用函數(shù) :
pthread_mutex_init: 用于初始化互斥鎖變量。
pthread_mutex_destroy: 用于銷毀互斥鎖對象。
pthread_mutex_lock: 用于加鎖,如果互斥鎖已被其他線程占用,則當前線程阻塞。
pthread_mutex_trylock: 嘗試加鎖,如果互斥鎖已被其他線程占用,則返回一個失敗狀態(tài)而不阻塞線程。
pthread_mutex_unlock: 用于解鎖,釋放互斥鎖使其他線程可以獲取。
3. 初始化線程鎖 :
有兩種方式可以對互斥鎖進行初始化:靜態(tài)初始化和動態(tài)初始化。
- 靜態(tài)初始化: 是在定義互斥鎖變量時直接進行初始化,不需要調(diào)用特定的初始化函數(shù)。
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
PTHREAD_MUTEX_INITIALIZER 是一個宏,用于靜態(tài)初始化互斥鎖變量。 - 動態(tài)初始化:動態(tài)初始化是在運行時使用初始化函數(shù)對互斥鎖進行初始化。
pthread_mutex_init(&mutex, NULL);
4. 示例代碼:
在下面代碼中,main 函數(shù)中有一個主線程 打印小寫字母,my_thread 為 子線程 打印 大寫字母。兩個線程通過互斥鎖來訪問 共享資源。
#include <stdio.h>
#include <pthread.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
pthread_mutex_t lock; // 創(chuàng)建 互斥鎖
void *my_thread(void *arg)
{
srand(time(NULL)); // 設(shè)置隨機種子
while(1)
{
pthread_mutex_lock(&lock);
printf("ABC ");
sleep(rand() % 3);
printf("XYZ\n");
pthread_mutex_unlock(&lock);
sleep(rand() % 3); // 休眠隨機秒,釋放cpu資源
}
pthread_exit(NULL);
}
int main(void)
{
pthread_t tid;
int ret;
srand(time(NULL)); // 設(shè)置隨機種子
ret = pthread_mutex_init(&lock, NULL); // 初始化互斥鎖
if(ret != 0)
{
printf("pthread_mutex_init err\n");
}
ret = pthread_create(&tid, NULL, my_thread, NULL);
if(ret != 0)
{
printf("pthread_create err\n");
}
while(1)
{
pthread_mutex_lock(&lock);
printf("abc ");
sleep(rand() % 3);
printf("xyz\n");
pthread_mutex_unlock(&lock);
sleep(rand() % 3);
}
pthread_mutex_destroy(&lock); // 銷毀 互斥鎖
pthread_join(tid,NULL); // 等待回收線程,獲取回收狀態(tài)
return 0;
}
注意 :鎖粒度(Lock Granularity):鎖的粒度應(yīng)該盡可能小,以避免鎖定過長時間,從而降低了并發(fā)性能。
三、死鎖
死鎖產(chǎn)生的原因:死鎖是指多個線程或進程因為彼此相互等待對方所持有的資源而無法繼續(xù)執(zhí)行的狀態(tài)。
解決:文章來源:http://www.zghlxwxcb.cn/news/detail-684682.html
- 使用資源的有序性:通過規(guī)定線程獲取資源的順序,避免出現(xiàn)循環(huán)等待的情況。例如,可以約定所有線程按照一定的順序獲取資源,從而避免死鎖的發(fā)生。
如果下面兩個線程 獲取資源的順序是相反的,則可能會產(chǎn)生死鎖。可以將 線程 B 先獲取 m1鎖,再獲取 m2鎖。
以下面代碼的方式獲取鎖,不會存在死鎖風險。文章來源地址http://www.zghlxwxcb.cn/news/detail-684682.html
#include <stdio.h>
#include <pthread.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
pthread_mutex_t lock1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t lock2 = PTHREAD_MUTEX_INITIALIZER;
void *my_thread1(void *arg)
{
pthread_mutex_lock(&lock1);
printf("my_thread1 : begin\n");
pthread_mutex_lock(&lock2);
printf("my_thread1 : end\n");
pthread_mutex_unlock(&lock2);
pthread_mutex_unlock(&lock1);
pthread_exit(NULL);
}
void *my_thread2(void *arg)
{
pthread_mutex_lock(&lock1);
printf("my_thread2 : begin\n");
pthread_mutex_lock(&lock2);
printf("my_thread2 : end\n");
pthread_mutex_unlock(&lock2);
pthread_mutex_unlock(&lock1);
pthread_exit(NULL);
}
int main(void)
{
pthread_t tid1,tid2;
int ret;
ret = pthread_create(&tid1, NULL, my_thread1, NULL);
if(ret != 0)
{
printf("pthread1_create err\n");
}
ret = pthread_create(&tid2, NULL, my_thread2, NULL);
if(ret != 0)
{
printf("pthread2_create err\n");
}
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
return 0;
}
- 設(shè)置超時機制:在請求資源時,設(shè)置一個超時時間,在超過該時間后如果仍未獲得資源,則放棄等待,釋放已經(jīng)獲取的資源,避免長時間的死鎖等待。
總結(jié)
到了這里,關(guān)于Linux 多線程同步機制(上)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!