Linux系統編程----16(線程同步,互斥量 mutex,互斥鎖的相關函數,死鎖,讀寫鎖)

同步概念

所謂同步,即同時起步,協調一致。不同的對象,對“同步”的理解方式略有不同。如,設備同步,是指在兩 個設備之間規定一個共同的時間參考;數據庫同步,是指讓兩個或多個數據庫內容保持一致,或者按需要部分保持 一致;文件同步,是指讓兩個或多個文件夾里的文件保持一致。等等
而,編程中、通信中所說的同步與生活中大家印象中的同步概念略有差異。“同”字應是指協同、協助、互相 配合。主旨在協同步調,按預定的先后次序運行。

線程同步

同步即協同步調,按預定的先后次序運行。
線程同步,指一個線程發出某一功能調用時,在沒有得到結果之前,該調用不返回。同時其它線程為保證數據 一致性,不能調用該功能。

  1. 舉例 1: 銀行存款 5000。柜臺,折:取 3000;提款機,卡:取 3000。剩余:2000

  2. 舉例 2: 內存中 100 字節,線程 T1 欲填入全 1, 線程 T2 欲填入全 0。但如果 T1 執行了 50 個字節失去 cpu,T2 執行,會將 T1 寫過的內容覆蓋。當 T1 再次獲得 cpu 繼續 從失去 cpu 的位置向后寫入 1,當執行結束,內存中的 100 字節,既不是全 1,也不是全 0。

    產生的現象叫做“與時間有關的錯誤”(time related)。為了避免這種數據混亂,線程需要同步。
    “同步”的目的,是為了避免數據混亂,解決與時間有關的錯誤。實際上,不僅線程間需要同步,進程間、信 號間等等都需要同步機制。
    因此,所有“多個控制流,共同操作一個共享資源”的情況,都需要同步。

數據混亂原因

  1. 資源共享(獨享資源則不會)
  2. 調度隨機(意味著數據訪問會出現競爭)
  3. 線程間缺乏必要的同步機制
  4. 以上 3 點中,前兩點不能改變,欲提高效率,傳遞數據,資源必須共享。只要共享資源,就一定會出現競爭。 只要存在競爭關系,數據就很容易出現混亂。
  5. 所以只能從第三點著手解決。使多個線程在訪問共享資源的時候,出現互斥。

互斥量 mutex

Linux 中提供一把互斥鎖 mutex(也稱之為互斥量) 。
每個線程在對資源操作前都嘗試先加鎖,成功加鎖才能操作,操作結束解鎖。

  1. 資源還是共享的,線程間也還是競爭的,
  2. 但通過“鎖”就將資源的訪問變成互斥操作,而后與時間有關的錯誤也不會再產生了。

在這里插入圖片描述
但,應注意:同一時刻,只能有一個線程持有該鎖。
當 A 線程對某個全局變量加鎖訪問,B 在訪問前嘗試加鎖,拿不到鎖,B 阻塞。C 線程不去加鎖,而直接訪問 該全局變量,依然能夠訪問,但會出現數據混亂。
所以,互斥鎖實質上是操作系統提供的一把“建議鎖”(又稱“協同鎖”),建議程序中有多線程訪問共享資源 的時候使用該機制。但,并沒有強制限定。
因此,即使有了 mutex,如果有線程不按規則來訪問數據,依然會造成數據混亂。

主要應用函數:

  1. pthread_mutex_init 函數
  2. pthread_mutex_destroy 函數
  3. pthread_mutex_lock 函數
  4. pthread_mutex_trylock 函數
  5. pthread_mutex_unlock 函數
  6. 以上 5 個函數的返回值都是:成功返回 0, 失敗返回錯誤號。
  7. pthread_mutex_t 類型,其本質是一個結構體。為簡化理解,應用時可忽略其實現細節,簡單當成整數看待。
  8. pthread_mutex_tmutex; 變量 mutex 只有兩種取值 1、0。

pthread_mutex_init 函數

初始化一個互斥鎖(互斥量)—> 初值可看作 1 int pthread_mutex_init (pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
參 1:傳出參數,調用時應傳 &mutex
restrict 關鍵字:只用于限制指針,告訴編譯器,所有修改該指針指向內存中內容的操作,只能通過本指針完成。 不能通過除本指針以外的其他變量或指針修改
參 2:互斥量屬性。是一個傳入參數,通常傳 NULL,選用默認屬性(線程間共享)。 參 APUE.12.4 同步屬性

  1. 靜態初始化:如果互斥鎖 mutex 是靜態分配的(定義在全局,或加了 static 關鍵字修飾),可以直接 使用宏進行初始化。e.g. pthead_mutex_tmuetx=PTHREAD_MUTEX_INITIALIZER;
  2. 動態初始化:局部變量應采用動態初始化。e.g. pthread_mutex_init(&mutex,NULL)

在這里插入圖片描述

pthread_mutex_destroy 函數

銷毀一個互斥鎖

int	pthread_mutex_destroy(pthread_mutex_t	*mutex);

pthread_mutex_lock 函數

加鎖成功。可理解為將 mutex–(或-1)

 int	pthread_mutex_lock(pthread_mutex_t	*mutex);

pthread_mutex_unlock 函數

解鎖成功。可理解為將 mutex++(或+1)

 int	pthread_mutex_unlock(pthread_mutex_t	*mutex);

pthread_mutex_trylock 函數

嘗試加鎖

int	pthread_mutex_trylock(pthread_mutex_t	*mutex);

在這里插入圖片描述

示例:子線程打印小寫,主控線程打印大寫
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
#include<stdlib.h>
#include<string.h>pthread_mutex_t mutex; //鎖的定義void *tfn(void *arg)
{srand(time(NULL));while(1){pthread_mutex_lock(&mutex);printf("hello");sleep(rand() % 3); /*模擬長時間操作共享資源,導致cpu易主,產生與時間>有關的錯誤*/printf("world\n");pthread_mutex_unlock(&mutex);sleep(rand() % 3); }   return NULL;
}int main(void)
{pthread_t tid;srand(time(NULL));pthread_mutex_init(&mutex,NULL); //mutex == 1pthread_create(&tid,NULL,tfn,NULL);while(1){pthread_mutex_lock(&mutex);printf("HELLO");sleep(rand()%3);printf("WORLD\n");pthread_mutex_unlock(&mutex);   sleep(rand() % 3);}pthread_mutex_destroy(&mutex);} 

在這里插入圖片描述

  1. 定義全局互斥量,初始化 init(&m,NULL)互斥量,添加對應的 destry
  2. 兩個線程 while 中,兩次 printf 前后,分別加 lock 和 unlock
  3. 將 unlock 挪至第二個 sleep 后,發現交替現象很難出現。
    線程在操作完共享資源后本應該立即解鎖,但修改后,線程抱著鎖睡眠。睡醒解鎖后又立即加鎖,這兩個 庫函數本身不會阻塞。
    所以在這兩行代碼之間失去 cpu 的概率很小。因此,另外一個線程很難得到加鎖的機會
  4. main 中加 flag=5 將 flg 在 while 中-- 這時,主線程輸出 5 次后試圖銷毀鎖,但子線程未將鎖釋放,無法 完成。
  5. main 中加 pthread_cancel()將子線程取消。
注意事項:在訪問共享資源前加鎖,訪問結束后立即解鎖。鎖的“粒度”應越小越好。

死鎖

  1. 線程試圖對同一個互斥量 A 加鎖兩次。
  2. 線程 1 擁有 A 鎖,請求獲得 B 鎖;線程 2 擁有 B 鎖,請求獲得 A 鎖
  3. 當不能獲得所有的鎖時,放棄已經占有的鎖

讀寫鎖

與互斥量類似,但讀寫鎖允許更高的并行性。其特性為:寫獨占,讀共享。

讀寫鎖狀態

一把讀寫鎖具備三種狀態:

  1. 讀模式下加鎖狀態 (讀鎖)
  2. 寫模式下加鎖狀態 (寫鎖)
  3. 不加鎖狀態

讀寫鎖特性:

  1. 讀寫鎖是“寫模式加鎖”時, 解鎖前,所有對該鎖加鎖的線程都會被阻塞。
  2. 讀寫鎖是“讀模式加鎖”時, 如果線程以讀模式對其加鎖會成功;如果線程以寫模式加鎖會阻塞。
  3. 讀寫鎖是“讀模式加鎖”時, 既有試圖以寫模式加鎖的線程,也有試圖以讀模式加鎖的線程。那么讀寫鎖 會阻塞隨后的讀模式鎖請求。優先滿足寫模式鎖。讀鎖、寫鎖并行阻塞,寫鎖優先級高
  4. 讀寫鎖也叫共享-獨占鎖。當讀寫鎖以讀模式鎖住時,它是以共享模式鎖住的;當它以寫模式鎖住時,它是以獨 占模式鎖住的。寫獨占、讀共享
  5. 讀寫鎖非常適合于對數據結構讀的次數遠大于寫的情況。

主要應用函數:

  1. pthread_rwlock_init 函數
  2. pthread_rwlock_destroy 函數
  3. pthread_rwlock_rdlock 函數
  4. pthread_rwlock_wrlock 函數
  5. pthread_rwlock_tryrdlock 函數
  6. pthread_rwlock_trywrlock 函數
  7. pthread_rwlock_unlock 函數
  8. 以上 7 個函數的返回值都是:成功返回 0, 失敗直接返回錯誤號。
  9. pthread_rwlock_t 類型 用于定義一個讀寫鎖變量。
  10. pthread_rwlock_trwlock;

pthread_rwlock_init 函數

初始化一把讀寫鎖
int pthread_rwlock_init(pthread_rwlock_t *restrictrwlock,const pthread_rwlockattr_t *restrictattr);
參 2:attr 表讀寫鎖屬性,通常使用默認屬性,傳 NULL 即可。

pthread_rwlock_destroy 函數

銷毀一把讀寫鎖

int	pthread_rwlock_destroy(pthread_rwlock_t	*rwlock);
pthread_rwlock_rdlock 函數

以讀方式請求讀寫鎖。(常簡稱為:請求讀鎖)

int	pthread_rwlock_rdlock(pthread_rwlock_t	*rwlock);
pthread_rwlock_wrlock 函數

以寫方式請求讀寫鎖。(常簡稱為:請求寫鎖)

int	pthread_rwlock_wrlock(pthread_rwlock_t	*rwlock);
pthread_rwlock_unlock 函數

解鎖

int	pthread_rwlock_unlock(pthread_rwlock_t		*rwlock);
pthread_rwlock_tryrdlock 函數

非阻塞以讀方式請求讀寫鎖(非阻塞請求讀鎖)

int	pthread_rwlock_tryrdlock(pthread_rwlock_t	*rwlock);
pthread_rwlock_trywrlock 函數

非阻塞以寫方式請求讀寫鎖(非阻塞請求寫鎖)

int	pthread_rwlock_trywrlock(pthread_rwlock_t		*rwlock);
同時有多個線程對同一全局數據讀、寫操作
#include<stdio.h>                                                            
#include<unistd.h>
#include<pthread.h>int counter;    //全局資源pthread_rwlock_t rwlock;void *th_write(void *arg) //寫線程
{int t;int i = (int)arg;while(1){t = counter;usleep(1000);pthread_rwlock_wrlock(&rwlock);printf("========write %d: %lu: counter=%d ++counter=%d\n",i,pthread_self(),t,++counter);pthread_rwlock_unlock(&rwlock);usleep(5000);}   return NULL;
}void *th_read(void *arg)
{int i = (int)arg;while(1){pthread_rwlock_rdlock(&rwlock);printf("---------read %d: %lu: %d\n",i,pthread_self(),counter);pthread_rwlock_unlock(&rwlock);usleep(900);}   return NULL;
}
int main(void)
{int i;pthread_t tid[8];pthread_rwlock_init(&rwlock,NULL);for(i = 0; i < 3; i++)pthread_create(&tid[i],NULL,th_write,(void *)i);for(i = 0; i < 5; i++)pthread_create(&tid[i+3],NULL,th_read,(void *)i);//三個寫線程,5個讀線程for(i = 0; i < 8; i++)pthread_join(tid[i],NULL);pthread_rwlock_destroy(&rwlock);   //釋放讀寫鎖return 0;
}        

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/383220.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/383220.shtml
英文地址,請注明出處:http://en.pswp.cn/news/383220.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

轉移字符的轉換

使得網頁上不會顯示 \x0a\x0a \x0a \x0a \x0a \x0a 類似的字符static int te_escape_isDec(char *ptr, unsigned int len) { …

Linux系統編程---17(條件變量及其函數,生產者消費者條件變量模型,生產者與消費者模型(線程安全隊列),條件變量優點,信號量及其主要函數,信號量與條件變量的區別,)

條件變量 條件變量本身不是鎖&#xff01;但它也可以造成線程阻塞。通常與互斥鎖配合使用。給多線程提供一個會合的場所。 主要應用函數&#xff1a; pthread_cond_init 函數pthread_cond_destroy 函數pthread_cond_wait 函數pthread_cond_timedwait 函數pthread_cond_signa…

好友

http://blog.csdn.net/liangyuannao/article/details/8583139

Linux系統編程---18(線程池相關概念及其實現)

線程池 概念&#xff1a; 一堆線程任務隊列 作用 避免大量線程頻繁的創建/銷毀時間成本避免瞬間大量線程創建耗盡資源&#xff0c;程序崩潰危險 實現 創建固定數量的線程創建一個線程安全的任務隊列 一種線程使用模式。 線程過多會帶來調度開銷&#xff0c;進而影響緩…

設計模式--1(設計模式基礎,設計模式基本原則,設計模式分類)

設計模式基礎 模式 在一定環境中解決某一問題的方案&#xff0c;包括三個基本元素–問題&#xff0c;解決方案和環境。大白話&#xff1a;在一定環境下&#xff0c;用固定套路解決問題。 設計模式 是一套被反復使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結。使…

source insight 使用技巧

source insight 使用技巧 1 sourceinsight screen font 的默認字體是Verdana的&#xff0c;它是一直變寬字體。在Document style中可以將字體改為定寬的Courier2 document options->auto indent 去掉indent Open Brace和Indent Close Brace的效果: 繼上一段&#xff0c;在…

設計模式----2(簡單工廠模式的概念,簡單工廠模式的實現,簡單工廠模式的優缺點)

簡單工廠模式 簡單工廠模式的概念 簡單工廠模式屬于類的創建型模式,又叫做靜態工廠方法模式。通過專門定義一個類來負 責創建其他類的實例&#xff0c;被創建的實例通常都具有共同的父類。 具體分類 工廠&#xff08;Creator&#xff09;角色 簡單工廠模式的核心&#xff0…

Redis常見問題及其一些重點知識總結

1、什么是 Redis&#xff1f;簡述它的優缺點&#xff1f; Redis 的全稱是&#xff1a;Remote Dictionary.Server&#xff0c;本質上是一個 Key-Value 類型的內存數據庫&#xff0c;很像 memcached&#xff0c;整個數據庫統統加載在內存當中進行操作&#xff0c;定期通過異步操…

shell生成隨機文件名

1 #!/bin/bash 2 # tempfile-name.sh: 臨時文件名產生器 3 4 BASE_STRmcookie # 32-字符的 magic cookie. 5 POS11 # 字符串中隨便的一個位置. 6 LEN5 # 取得 $LEN 長度連續的字符串. 7 8 prefixtemp # 最終的一個臨時文…

設計模式---3(工廠方法模式的概念,工廠方法模式的實現,工廠方法模式和簡單工廠模式比較)

工廠方法模式 概念 工廠方法模式同樣屬于類的創建型模式又被稱為多態工廠模式 。 工廠方法模式的意義 定義一個創建產品對象的工廠接口&#xff0c;將實際創建工作推遲到子類當中。 核心工廠類不再負責產品的創建&#xff0c;這樣核心類成為一個抽象工廠角色&#xff0c;僅…

設計模式---4(抽象工廠模式的概念,產品組和產品等級的概念,抽象工廠模式的實現)

抽象工廠模式 抽象工廠模式的概念 抽象工廠模式是所有形態的工廠模式中最為抽象和最其一般性的。抽象工廠模式可以向 客戶端提供一個接口&#xff0c;使得客戶端在不必指定產品的具體類型的情況下&#xff0c;能夠創建多個產品 族的產品對象。 抽象工廠的角色及其職責 抽象工…

Win32項目關于MessageBox參數的詳細說明

函數功能&#xff1a;該函數創建、顯示、和操作一個消息框。消息框含有應用程序定義的消息和標題&#xff0c;加上預定義圖標與Push&#xff08;下按&#xff09;按鈕的任何組合。 函數原型&#xff1a;int MessageBox(HWND hWnd,LPCTSTR IpCaption,UINT…

w3af解析

1. w3af簡介 w3afis a Web Application Attack and Audit Framework.即Web應用攻擊和審計框架。w3af用python編寫&#xff0c;依賴的庫主要有2類&#xff0c;分別如下&#xff1a; <1> Core requirements: Python 2.6 fpconst-0.7.2&#xff1a;用于處理IEEE 754浮點…

1.c++中初始化列表和構造函數初始化的區別是什么?2.類的成員變量的初始化順序是按照聲明順序嗎?

初始化列表和構造函數初始化的區別是什么&#xff1f; 初始化和賦值對內置類型的成員沒有太大的區別&#xff0c;在成員初始化列表和構造函數體內進行&#xff0c;在性能和結果上都是一樣的。只有一些需要注意的事項 初始化列表一般情況如下&#xff1a; Date(int year, int …

設計模式---5(建造者模式的概念及其實現,建造者模式的角色與職責,建造者模式和工廠模式的區別)

建造者模式 建造者模式的概念 Builder 模式也叫建造者模式或者生成器模式&#xff0c;是由 GoF 提出的 23 種設計模式中的一種。 Builder 模式是一種對象創建型模式之一&#xff0c;用來隱藏復合對象的創建過程&#xff0c;它把復合對象的 創建過程加以抽象&#xff0c;通過子…

system阻塞SIGCHLD信號原因

system阻塞SIGCHLD信號原因 標簽&#xff1a; c 2014-11-08 11:58 198人閱讀 評論(0) 收藏 舉報 分類&#xff1a; linux編程&#xff08;1&#xff09; 代碼1&#xff1a;APUE10.18節的system函數源代碼 int system(const char *cmdstring) /* with appropriate signal ha…

設計模式6---(單例模式的概念及其實現(懶漢式和餓漢式),線程安全)

單例模式 單例模式的概念 單例模式是一種對象創建型模式&#xff0c;使用單例模式&#xff0c;可以保證為一個類只生成唯一的實例對象。也就是說&#xff0c;在整個程序空間中&#xff0c;該類只存在一個實例對象。 GoF 對單例模式的定義是&#xff1a;保證一個類、只有一個實…

C語言解析http請求表單內容

[1].[文件] cgi.h ~ 405B 下載(105) 跳至 [1] [2] [3] [4] [5] [6] [7] [8] ?123456789101112131415161718192021222324252627#ifndef CGI_H#define CGI_H#include <stdio.h>#include <string.h>#include <stdlib.h>typedef struct Node{char *…

centos給用戶添加sudo權限

linux給用戶添加sudo權限&#xff1a; 有時候&#xff0c;linux下面運行sudo命令&#xff0c;會提示類似&#xff1a; xxxis not in the sudoers file. This incident will be reported. 這里&#xff0c;xxx是用戶名稱&#xff0c;然后導致無法執行sudo命令&#xff0c;這時候…

php手冊

http://www.php100.com/manual/php/ http://www.kuqin.com/php5_doc/