[TOC]
# 簡介
而在線程里也有這么一把鎖:互斥鎖(mutex),也叫互斥量,互斥鎖是一種簡單的加鎖的方法來控制對共享資源的訪問,互斥鎖只有兩種狀態,即**加鎖**( lock )和**解鎖**( unlock )。
互斥鎖的操作流程如下:
1)在訪問共享資源后臨界區域前,對互斥鎖進行加鎖。
2)在訪問完成后釋放互斥鎖導上的鎖。
3)對互斥鎖進行加鎖后,任何其他試圖再次對互斥鎖加鎖的線程將會被阻塞,直到鎖被釋放。
互斥鎖的數據類型是: pthread\_mutex\_t。
安裝對應幫助手冊:
> $ sudo apt-get install -y manpages-posix-dev
# pthread\_mutex\_init函數
初始化互斥鎖:
~~~
#include <pthread.h>
?
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
功能:
初始化一個互斥鎖。
參數:
mutex:互斥鎖地址。類型是 pthread_mutex_t 。
attr:設置互斥量的屬性,通常可采用默認屬性,即可將 attr 設為 NULL。
?
可以使用宏 PTHREAD_MUTEX_INITIALIZER 靜態初始化互斥鎖,比如:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
?
這種方法等價于使用 NULL 指定的 attr 參數調用 pthread_mutex_init() 來完成動態初始化,不同之處在于 PTHREAD_MUTEX_INITIALIZER 宏不進行錯誤檢查。
?
返回值:
成功:0,成功申請的鎖默認是打開的。
失敗:非 0 錯誤碼
~~~
# pthread\_mutex\_destroy函數
~~~
#include <pthread.h>
?
int pthread_mutex_destroy(pthread_mutex_t *mutex);
功能:
銷毀指定的一個互斥鎖。互斥鎖在使用完畢后,必須要對互斥鎖進行銷毀,以釋放資源。
參數:
mutex:互斥鎖地址。
返回值:
成功:0
失敗:非 0 錯誤碼
~~~
?
# pthread_mutex_lock函數
~~~
#include <pthread.h>
?
int pthread_mutex_lock(pthread_mutex_t *mutex);
功能:
對互斥鎖上鎖,若互斥鎖已經上鎖,則調用者阻塞,直到互斥鎖解鎖后再上鎖。
參數:
mutex:互斥鎖地址。
返回值:
成功:0
失敗:非 0 錯誤碼
?
int pthread_mutex_trylock(pthread_mutex_t *mutex);
* 調用該函數時,若互斥鎖未加鎖,則上鎖,返回 0;
* 若互斥鎖已加鎖,則函數直接返回失敗,即 EBUSY。
~~~
# pthread_mutex_unlock函數
~~~
#include <pthread.h>
?
int pthread_mutex_unlock(pthread_mutex_t *mutex);
功能:
對指定的互斥鎖解鎖。
參數:
mutex:互斥鎖地址。
返回值:
成功:0
失敗:非0錯誤碼
?~~~
# 例子
?~~~
pthread_mutex_t mutex; //互斥鎖
// 打印機
void printer(char *str)
{
pthread_mutex_lock(&mutex); //上鎖
while (*str != '\0')
{
putchar(*str);
fflush(stdout);
str++;
sleep(1);
}
printf("\n");
pthread_mutex_unlock(&mutex); //解鎖
}
?
// 線程一
void *thread_fun_1(void *arg)
{
char *str = "hello";
printer(str); //打印
}
?
// 線程二
void *thread_fun_2(void *arg)
{
char *str = "world";
printer(str); //打印
}
?
int main(void)
{
pthread_t tid1, tid2;
?
pthread_mutex_init(&mutex, NULL); //初始化互斥鎖
?
// 創建 2 個線程
pthread_create(&tid1, NULL, thread_fun_1, NULL);
pthread_create(&tid2, NULL, thread_fun_2, NULL);
?
// 等待線程結束,回收其資源
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
?
pthread_mutex_destroy(&mutex); //銷毀互斥鎖
?
return 0;
}
~~~
# 死鎖
**死鎖引起的原因**
競爭不可搶占資源引起死鎖
也就是我們說的第一種情況,而這都在等待對方占有的不可搶占的資源。
競爭可消耗資源引起死鎖
有p1,p2,p3三個進程,p1向p2發送消息并接受p3發送的消息,p2向p3發送消息并接受p1的消息,p3向p1發送消息并接受p2的消息,如果設置是先接到消息后發送消息,則所有的消息都不能發送,這就造成死鎖。
進程推進順序不當引起死鎖
有進程p1,p2,都需要資源A,B,本來可以p1運行A --> p1運行B --> p2運行A --> p2運行B,但是順序換了,p1運行A時p2運行B,容易發生第一種死鎖。互相搶占資源。
**3)死鎖的必要條件**
互斥條件
某資源只能被一個進程使用,其他進程請求該資源時,只能等待,直到資源使用完畢后釋放資源。
請求和保持條件
程序已經保持了至少一個資源,但是又提出了新要求,而這個資源被其他進程占用,自己占用資源卻保持不放。
不可搶占條件
進程已獲得的資源沒有使用完,不能被搶占。
循環等待條件
必然存在一個循環鏈。
**4)處理死鎖的思路**
預防死鎖
破壞死鎖的四個必要條件中的一個或多個來預防死鎖。
避免死鎖
和預防死鎖的區別就是,在資源動態分配過程中,用某種方式防止系統進入不安全的狀態。
檢測死鎖
運行時出現死鎖,能及時發現死鎖,把程序解脫出來
解除死鎖
發生死鎖后,解脫進程,通常撤銷進程,回收資源,再分配給正處于阻塞狀態的進程。
**5)預防死鎖的方法**
***破壞請求和保持條件***
協議1:
所有進程開始前,必須一次性地申請所需的所有資源,這樣運行期間就不會再提出資源要求,破壞了請求條件,即使有一種資源不能滿足需求,也不會給它分配正在空閑的資源,這樣它就沒有資源,就破壞了保持條件,從而預防死鎖的發生。
協議2:
允許一個進程只獲得初期的資源就開始運行,然后再把運行完的資源釋放出來。然后再請求新的資源。
***破壞不可搶占條件***
當一個已經保持了某種不可搶占資源的進程,提出新資源請求不能被滿足時,它必須釋放已經保持的所有資源,以后需要時再重新申請。
***破壞循環等待條件***
對系統中的所有資源類型進行線性排序,然后規定每個進程必須按序列號遞增的順序請求資源。假如進程請求到了一些序列號較高的資源,然后有請求一個序列較低的資源時,必須先釋放相同和更高序號的資源后才能申請低序號的資源。多個同類資源必須一起請求。
- c語言
- 基礎知識
- 變量和常量
- 宏定義和預處理
- 隨機數
- register變量
- errno全局變量
- 靜態變量
- 類型
- 數組
- 類型轉換
- vs中c4996錯誤
- 數據類型和長度
- 二進制數,八進制數和十六進制數
- 位域
- typedef定義類型
- 函數和編譯
- 函數調用慣例
- 函數進棧和出棧
- 函數
- 編譯
- sizeof
- main函數接收參數
- 宏函數
- 目標文件和可執行文件有什么
- 強符號和弱符號
- 什么是鏈接
- 符號
- 強引用和弱引用
- 字符串處理函數
- sscanf
- 查找子字符串
- 字符串指針
- qt
- MFC
- 指針
- 簡介
- 指針詳解
- 案例
- 指針數組
- 偏移量
- 間接賦值
- 易錯點
- 二級指針
- 結構體指針
- 字節對齊
- 函數指針
- 指針例子
- main接收用戶輸入
- 內存布局
- 內存分區
- 空間開辟和釋放
- 堆空間操作字符串
- 內存處理函數
- 內存分頁
- 內存模型
- 棧
- 棧溢出攻擊
- 內存泄露
- 大小端存儲法
- 寄存器
- 結構體
- 共用體
- 枚舉
- 文件操作
- 文件到底是什么
- 文件打開和關閉
- 文件的順序讀寫
- 文件的隨機讀寫
- 文件復制
- FILE和緩沖區
- 文件大小
- 插入,刪除,更改文件內容
- typeid
- 內部鏈接和外部鏈接
- 動態庫
- 調試器
- 調試的概念
- vs調試
- 多文件編程
- extern關鍵字
- 頭文件規范
- 標準庫以及標準頭文件
- 頭文件只包含一次
- static
- 多線程
- 簡介
- 創建線程threads.h
- 創建線程pthread
- gdb
- 簡介
- mac使用gdb
- setjump和longjump
- 零拷貝
- gc
- 調試器原理
- c++
- c++簡介
- c++對c的擴展
- ::作用域運算符
- 名字控制
- cpp對c的增強
- const
- 變量定義數組
- 盡量以const替換#define
- 引用
- 內聯函數
- 函數默認參數
- 函數占位參數
- 函數重載
- extern "C"
- 類和對象
- 類封裝
- 構造和析構
- 深淺拷貝
- explicit關鍵字
- 動態對象創建
- 靜態成員
- 對象模型
- this
- 友元
- 單例
- 繼承
- 多態
- 運算符重載
- 賦值重載
- 指針運算符(*,->)重載
- 前置和后置++
- 左移<<運算符重載
- 函數調用符重載
- 總結
- bool重載
- 模板
- 簡介
- 普通函數和模板函數調用
- 模板的局限性
- 類模板
- 復數的模板類
- 類模板作為參數
- 類模板繼承
- 類模板類內和類外實現
- 類模板和友元函數
- 類模板實現數組
- 類型轉換
- 異常
- 異常基本語法
- 異常的接口聲明
- 異常的棧解旋
- 異常的多態
- 標準異常庫
- 自定義異常
- io
- 流的概念和類庫結構
- 標準io流
- 標準輸入流
- 標準輸出流
- 文件讀寫
- STL
- 簡介
- string容器
- vector容器
- deque容器
- stack容器
- queue容器
- list容器
- set/multiset容器
- map/multimap容器
- pair對組
- 深淺拷貝問題
- 使用時機
- 常用算法
- 函數對象
- 謂詞
- 內建函數對象
- 函數對象適配器
- 空間適配器
- 常用遍歷算法
- 查找算法
- 排序算法
- 拷貝和替換算法
- 算術生成算法
- 集合算法
- gcc
- GDB
- makefile
- visualstudio
- VisualAssistX
- 各種插件
- utf8編碼
- 制作安裝項目
- 編譯模式
- 內存對齊
- 快捷鍵
- 自動補全
- 查看c++類內存布局
- FFmpeg
- ffmpeg架構
- 命令的基本格式
- 分解與復用
- 處理原始數據
- 錄屏和音
- 濾鏡
- 水印
- 音視頻的拼接與裁剪
- 視頻圖片轉換
- 直播
- ffplay
- 常見問題
- 多媒體文件處理
- ffmpeg代碼結構
- 日志系統
- 處理流數據
- linux
- 系統調用
- 常用IO函數
- 文件操作函數
- 文件描述符復制
- 目錄相關操作
- 時間相關函數
- 進程
- valgrind
- 進程通信
- 信號
- 信號產生函數
- 信號集
- 信號捕捉
- SIGCHLD信號
- 不可重入函數和可重入函數
- 進程組
- 會話
- 守護進程
- 線程
- 線程屬性
- 互斥鎖
- 讀寫鎖
- 條件變量
- 信號量
- 網絡
- 分層模型
- 協議格式
- TCP協議
- socket
- socket概念
- 網絡字節序
- ip地址轉換函數
- sockaddr數據結構
- 網絡套接字函數
- socket模型創建流程圖
- socket函數
- bind函數
- listen函數
- accept函數
- connect函數
- C/S模型-TCP
- 出錯處理封裝函數
- 多進程并發服務器
- 多線程并發服務器
- 多路I/O復用服務器
- select
- poll
- epoll
- epoll事件
- epoll例子
- epoll反應堆思想
- udp
- socket IPC(本地套接字domain)
- 其他常用函數
- libevent
- libevent簡介