linux線程同步(2)-條件變數

来源:http://www.cnblogs.com/yuuyuu/archive/2016/01/18/5140875.html
-Advertisement-
Play Games

一.概述 上一篇,介紹了互斥量。條件變數與互斥量不同,互斥量是防止多線程同時訪問共用的互斥變數來保護臨界區。條件變數是多線程間可以通過它來告知其他線程某個狀態發生了改變,讓等待在這個條件變數的線程繼續執行。通俗一點來講:設置一個條件變數讓線程1等待在一...


一.概述                                                   

上一篇,介紹了互斥量。條件變數與互斥量不同,互斥量是防止多線程同時訪問共用的互斥變數來保護臨界區。條件變數是多線程間可以通過它來告知其他線程某個狀態發生了改變,讓等待在這個條件變數的線程繼續執行。通俗一點來講:設置一個條件變數讓線程1等待在一個臨界區的前面,當其他線程給這個變數執行通知操作時,線程1才會被喚醒,繼續向下執行。

條件變數總是和互斥量一起使用,互斥量保護著條件變數,防止多個線程對條件變數產生競爭。等會寫個小例子,看它們如何一起合作!

二.函數介面                                            

1.初始化條件變數

1.1:巨集常量初始化

1 pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

1.2:函數初始化

1 #include <pthread.h>
2 
3 int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);

跟互斥量類似,cond是條件變數的結構指針,attr是條件變數屬性的結構指針。

2.等待和通知條件變數

1 #include <pthread.h>
2 
3 int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
4 int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
5 
6 int pthread_cond_broadcast(pthread_cond_t *cond);
7 int pthread_cond_signal(pthread_cond_t *cond);

等待函數裡面,要傳入一個互斥量。pthread_cond_timewait()可以指定一個時間來等待,如果規定的時間沒有獲得通知,就返回ETIMEDOUT錯誤。而pthread_cond_wait()會一直阻塞

通知函數,pthread_cond_signal()至少喚醒一個等待的線程,pthread_cond_broadcast()會喚醒在該條件變數上所有線程。

3.銷毀條件變數

1 #include <pthread.h>
2 
3 int pthread_cond_destroy(pthread_cond_t *cond);

三.簡單的例子                                       

我們還是用上一篇互斥量的例子。單獨使用互斥量時,有些線程要獲取某個狀態的成立,需要多次進出臨界區,對互斥量頻繁加鎖解鎖造成系統資源的浪費。下麵結合條件變數來解決這個問題:

  1 /**
  2  * @file pthread_mutex.c
  3  */
  4 
  5 #include <stdio.h>
  6 #include <stdlib.h>
  7 #include <string.h>
  8 #include <unistd.h>
  9 #include <pthread.h>
 10 
 11 /* 定義互斥量 */
 12 pthread_mutex_t mtx;
 13 /* 互斥量屬性 */
 14 pthread_mutexattr_t mtx_attr;
 15 /* 全局資源 */
 16 int money;
 17 
 18 /* 條件變數 */
 19 pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
 20 
 21 void err_exit(const char *err_msg)
 22 {
 23     printf("error:%s\n", err_msg);
 24     exit(1);
 25 }
 26 
 27 /* 線程函數 */
 28 void *thread_fun(void *arg)
 29 {
 30     while (1)
 31     {
 32         /* 加鎖 */
 33         pthread_mutex_lock(&mtx);
 34 
 35         /* 條件變數 */
 36         while (money > 0)
 37         {
 38             printf("子線程坐等money等於0...\n");
 39             pthread_cond_wait(&cond, &mtx);
 40         }
 41 
 42         printf("子線程進入臨界區查看money\n");
 43         if (money == 0)
 44         {
 45             money += 200;
 46             printf("子線程:money = %d\n", money);
 47         }
 48 
 49         /* 解鎖 */
 50         pthread_mutex_unlock(&mtx);
 51 
 52         sleep(1);
 53     }
 54 
 55     return NULL;
 56 }
 57 
 58 int main(void)
 59 {
 60     pthread_t tid;
 61 
 62     /* 初始化互斥量屬性 */
 63     if (pthread_mutexattr_init(&mtx_attr) == -1)
 64         err_exit("pthread_mutexattr_init()");
 65 
 66     /* 設置互斥量屬性 */
 67     if (pthread_mutexattr_settype(&mtx_attr, PTHREAD_MUTEX_NORMAL) == -1)
 68         err_exit("pthread_mutexattr_settype()");
 69 
 70     /* 初始化互斥量 */
 71     if (pthread_mutex_init(&mtx, &mtx_attr) == -1)
 72         err_exit("pthread_mutex_init()");
 73 
 74     /* 創建一個線程 */
 75     if (pthread_create(&tid, NULL, thread_fun, NULL)== -1)
 76         err_exit("pthread_create()");
 77 
 78     money = 1000;
 79     while (1)
 80     {
 81         /* 加鎖 */
 82         pthread_mutex_lock(&mtx);
 83 
 84         if (money > 0)
 85         {
 86             money -= 100;
 87             printf("主線程:money = %d\n", money);
 88         }
 89 
 90         /* 解鎖 */
 91         pthread_mutex_unlock(&mtx);
 92 
 93         /* 如果money = 1,就通知子線程 */
 94         if (money == 0)
 95         {
 96             printf("通知子線程\n");
 97             pthread_cond_signal(&cond);
 98         }
 99 
100         sleep(1);
101     }
102 
103     return 0;
104 }

代碼跟上一個例子幾乎一樣,就加了一個條件變數。編譯運行:

可以看到第39行的等待條件變數觸發後,子線程會一直等待,直到主線程通知它。這樣子線程就不會頻繁進入臨界區,頻繁加鎖解鎖。

四.深入知識                                             

1.等待函數裡面要傳入一個互斥量,這個互斥量會在這個函數調用時會發生如下變化:函數剛剛被調用時,會把這個互斥量解鎖,然後讓調用線程阻塞,解鎖後其他線程才有機會獲得這個鎖。當某個線程調用通知函數時,這個函數收到通知後,又把互斥量加鎖,然後繼續向下操作臨界區。可見這個設計是非常合理的!!!

2.條件變數的等待函數用while迴圈包圍,本程式的第36行。原因:如果有多個線程都在等待這個條件變數關聯的互斥量,當條件變數收到通知,它下一步就是要鎖住這個互斥量,但在這個極小的時間差裡面,其他線程搶先獲取了這互斥量併進入臨界區把某個狀態改變了。此時這個條件變數應該繼續判斷別人剛剛搶先修改的狀態,即繼續執行while的判斷。還有一個原因時防止虛假通知,收到虛假通知後,只要while裡面的條件為真,就繼續休眠!!!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • USE[test_YTHH]GO/******Object:StoredProcedure[dbo].[usp_Print_SCC_Menu]ScriptDate:04/08/201311:21:23******/SETANSI_NULLSONGOSETQUOTED_IDENTIFIERONGO——...
  • Sometimes when we attempting to login the SQL Server 20xx Management Studio, when we type in the correct password, it fails with: "A network-related ....
  • 技術若只如初見,那麼還會踩坑麽? 在系統引入 MongoDB 也有幾年了,一開始是因為 MySQL 中有單表記錄增長太快(每天幾千萬條吧)容易拖慢 MySQL 的主從複製。而這類數據增長迅速的流水錶,對數據一致性也沒那麼高要求,而且業務上也不需要關聯查詢它,就考慮分出去。為什麼是 M...
  • 在MySQL中間件出現之前,對於MySQL主從集群,如果要實現其讀寫分離,一般是在程式端實現,這樣就帶來一個問題,即資料庫和程式的耦合度太高,如果我資料庫的地址發生改變了,那麼我程式端也要進行相應的修改,如果資料庫不小心掛掉了,則同時也意味著程式的不可用,而這對很多應用來說,並不能接受。引入MySQ...
  • Linq分頁的方法用到Skip(),Take()。然而,用SQL腳本進行分頁如何寫呢?首先我們可以通過ROW_NUMBER() OVER進行排序並得到一個帶序號的視圖,再通過序號確定要查找的分頁數據例: 1 DECLARE @pageSize INT ; 2 DECLARE @pageIndex I...
  • 本文目錄列表:1、位運算2、設置日曆數據表節假日標誌3、總結語4、參考清單列表位運算SQL Server支持的按位運算符有三個,分別為:按位與(&)、按位或(|)、按位異或(^)。位運算符用於int、smallint或tinyint數據,目前SQL Server能支持的按位運算的最大整數類型為Int...
  • 該文章為原創,日後可能會根據實際開發經驗和網友評論,進行相應地方修改,為獲得最新博客動態,望在轉發博客的時候註明出處。觸發器要實現的功能:(1)獲取對錶Table1數據操作操作類型(insert、delete或update)。(2)將表修改後的數據保存到表Table2(該表結構與Table1表結構類...
  • 一. 將bootloader燒入SD卡 1.格式化SD卡(不掛載): fdisk /dev/sdx -d 刪除 -n 新建分區 -w 保存退出 6~default sdx1 兩種格式化方式: mkfs -t vfat /dev/sdx1 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...