pthread小結

来源:https://www.cnblogs.com/willhua/archive/2018/12/02/10053769.html
-Advertisement-
Play Games

"參考1 https://computing.llnl.gov/tutorials/pthreads/" "參考2 http://man7.org/linux/man pages/man7/pthreads.7.html" join 阻塞調用線程,直至指定pthread_t線程終止 在同一個線程中重 ...


參考1 https://computing.llnl.gov/tutorials/pthreads/
參考2 http://man7.org/linux/man-pages/man7/pthreads.7.html


join

  • int pthread_join(pthread_t, void**);阻塞調用線程,直至指定pthread_t線程終止
  • 在同一個線程中重覆調用join會導致錯誤
  • 在創建線程的時候可以指定要創建的線程是否joinable,如果是,則可以join,否則(即detached)不可以。一般預設都是joinable
  • POSIX指出線程should指定為joinable
  • 如果確定一個線程需要join,那麼最好明確指定該線程joinable,通過如下四步:
  1. Declare a pthread attribute variable of the pthread_attr_t data type
  2. Initialize the attribute variable with pthread_attr_init()
  3. Set the attribute detached status with pthread_attr_setdetachstate()
  4. When done, free library resources used by the attribute with pthread_attr_destroy()
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
for(t=0; t<NUM_THREADS; t++) {
   printf("Main: creating thread %ld\n", t);
   rc = pthread_create(&thread[t], &attr, BusyWork, (void *)t);  //一個attr可以給多個線程使用
   if (rc) {
      printf("ERROR; return code from pthread_create() is %d\n", rc);
      exit(-1);
      }
   }
pthread_attr_destroy(&attr);    //記得釋放資源。create執行完之後就可以釋放,而不用等待線程結束
  • 如果確定一個線程不需要joinable,那麼應該明確考慮設置屬性為detached
  • 通過pthread_detach()來設置線程為不可join,即使它被創建的時候被設置為joinable。這個動作不可逆

    stack

  • POSIX沒有規定創建的線程的stack大小是多少,這是由implementation決定的
  • pthread_attr_setstacksize可以用來設置需要的stack大小
  • pthread_attr_getstackaddrpthread_attr_setstackaddr可以用來設置stack需要放置到特定的記憶體區域
size_t stacksize;
pthread_attr_init(&attr);
pthread_attr_getstacksize (&attr, &stacksize);
printf("Default stack size = %li\n", stacksize);
size = 10000; //設置為10000bytes
pthread_attr_setstacksize (&attr, stacksize);
printf("set stack size = %li\n", stacksize);
pthread_create(&threads[t], &attr, dowork, (void *)t);

mutex

Creating and Destroying Mutexes

//destroy,成功則返回0
int pthread_mutex_destroy(pthread_mutex_t *mutex);
//動態初始化,成功則返回0. 如果attr為NULL,那麼將使用預設屬性,相當於PTHREAD_MUTEX_INITIALIZER
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
//使用預設參數靜態初始化
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

//mutex屬性
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
int pthread_mutexattr_init(pthread_mutexattr_t *attr);
  • 被destroy的mutex可以使用pthread_mutex_init重新初始化
  • destroy一個處於lock狀態的mutex,將會導致undefined行為
  • 只有mutex可以用來執行synchronization,用它的copies來執行lock,unlock和trylock將導致undefined
  • 不可以重覆初始化已經初始化了的mutex

    Locking and Unlocking Mutexes

//如果別的線程已經lock,那會一直阻塞當前線程直至獲得鎖
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
mutex類型 性質
PTHREAD_MUTEX_NORMAL 對mutex的重覆lock,即本線程已經lock了mutex,在沒有unlock之前又嘗試lock,將導致死鎖行為;unlock一個沒有被本線程lock或者沒有被任何線程lock的mutex,將導致未定義行為
PTHREAD_MUTEX_ERRORCHECK 嘗試重覆lock一個mutex將不會死鎖,而是返回一個錯誤值;unlock一個沒有被本線程lock或者沒有被任何線程lock的mutex,也會返回錯誤值
PTHREAD_MUTEX_RECURSIVE mutex可以被重覆lock。每次lock會增加相關計數,直至通過unlock使計數達到0時,才可以被別的線程lock;unlock一個沒有被本線程lock或者沒有被任何線程lock的mutex,也會返回錯誤值
PTHREAD_MUTEX_DEFAULT 重覆lock會導致未定義行為(NORMAL中會導致死鎖);unlock一個沒有被本線程lock或者沒有被任何線程lock的mutex,也將導致未定義行為。 不過,在NDK的定義中,直接把PTHREAD_MUTEX_DEFAULT = PTHREAD_MUTEX_NORMAL
  • pthread_mutex_trylockpthread_mutex_lock只有一點區別:如果當前mutex被任意線程lock,pthread_mutex_trylock都將會立刻返回。如果mutex是PTHREAD_MUTEX_RECURSIVE的,且mutex已經被當前調用線程lock,pthread_mutex_trylock也同樣會導致計數增一,並返回success。
  • 如果正在等待lock的線程收到了一個signal,當其從signal handler返回之後,會繼續等待lock,就和signal沒有發生一樣
  • 除非使用了 thread priority scheduling,否則多個正在等待lock的線程獲得lock的情況可能多少有點random
  • 如果成功,這三個函數都是返回0,否則返回相應的error

Condition Variables

  • mutex通過控制對數據的訪問許可權來達到同步;而condition variables則基於數據的值來控制同步
  • 如果不使用condition variable,線程想要檢查某個條件則只能通過輪詢的方式,這將非常resource consuming,因為這期間線程將一直active。而使用condition variable則將在不使用輪詢的情況下實現此目標
  • condition variable 經常和mutex一起使用

    Creating and Destroying Condition Variables

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_condattr_destroy(pthread_condattr_t *attr);
int pthread_condattr_init(pthread_condattr_t *attr);
  • destory正由某個線程用於block的cv將導致未定義行為
  • 只有cv自己能夠用於同步,任何基於它的copies調用pthread_cond_wait(), pthread_cond_timedwait(), pthread_cond_signal(), pthread_cond_broadcast(), pthread_cond_destroy()都會產生未定義行為
  • 初始化一個已經初始化的cv會導致未定義行為;已經destory的cv可以再次初始化;

Waiting and Signaling on Condition Variables

一般使用流程:
FueOfg.jpg

int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, 
                            const struct timespec *restrict abstime);
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
  • 這兩個函數將導致調用線程block on the condition variable, 並且需要傳入一個由調用線程lock了的mutex,否則導致未定義行為
  • 如果在wait之前,沒有明確lock對應mutex,可能並不會導致block
  • 這兩個函數會原子的unlock the mutex,並且導致調用線程block on the condition variable。這裡的原子的意味著:只要其他線程lock了這個mutex,那麼這個線程對pthread_cond_broadcast()pthread_cond_signal()的調用都會產生調用wait的線程已經blocked on the condition variable的效果
  • 只要這兩個函數返回,那麼調用線程就已經lock了這個mutex
  • 虛假喚醒Spurious wakeups 可能會產生,而且這並不違反標準,所以,即使調用線程被喚醒,也不意味著對某個值做出某種保證,應該再次確認條件是否真的滿足了。同時,考慮到線程之間的競爭,pthread_cond_timedwait由於超時返回之後,條件也可能已經滿足。總之。任何時候wait返回,都需要重新評估條件是否滿足,這點非常重要
  • 一旦線程waits on the condition variable,那麼這個cv就和相應的mutex綁定了,在wait返回之前,不能再使用另外的mutex來調用wait,這會導致未定義行為
  • condition wait是一個cancellation point未明白
  • 假設一個由於wait調用而block線程由於被canceled而unblocked,這個不會consume任何condition signal。
  • pthread_cond_timedwait()pthread_cond_wait()是equivalent的,除了:當signaled或者broadcasted超過指定時間,pthread_cond_timedwait()就會返回返回error。同時,cv還可以支持 Clock Selection,選擇不同的Clock來measure指定的時間
  • 當cv wait期間,一個signal產生了,那麼cv可能會繼續wait就像沒有中斷一樣,或者這會形成一個spurious wakeup,返回0.
    推薦使用while迴圈替代if語句來檢查當前條件是否真的滿足,有如下三點好處:
  1. 如果有多個線程都是在wait相同過的wake up signal,那麼當其他任意一個被waked up之後,他們都有可能更改條件值,而導致條件不滿足
  2. 線程可能會因為程式bug而收到一個signal
  3. Pthreads library被容許產生虛假喚醒,而且這並不違反標準
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
  • 當多於一個線程在wait的時候,應該使用pthread_cond_broadcast()
  • 在signal之前,應該先lock對應mutex,然後在signal之後,應該unlock對應mutex。如果忘記了unlock,那麼相應的wait線程會繼續blocked,因為他們無法獲得lock

結束線程

有:

  • pthread_cancel
  • pthread_exit
  • pthread_kill
    參考https://www.cnblogs.com/biyeymyhjob/archive/2012/10/11/2720377.html
    ---

    其他函數

    pthread_self

    pthread_t pthread_self(void);返回調用線程的thread id

    pthread_equal

    int pthread_equal(pthread_t t1, pthread_t t2);比較兩個ID是否相等,如果相等則返回not-zero value不相等則返回0。由於pthread_t結果opaque,所以不應該用==來比較

    pthread_once

    int pthread_once(pthread_once_t *once_control, void (*init_routine)(void));:在進程中,任何首次調用這個函數的線程,在pthread_once_t once_control = PTHREAD_ONCE_INIT的時候,會調用init_routine程式。並且當此函數返回的時候,init_routine已經執行完了(這裡沒有說init_routine會阻塞調用線程,可能考慮的是,當線程A已經調用init_routine,而另外一個線程B也調用了pthread_once,那麼是否B也會等待A調用的init_routine執行完畢?)。如果成功完成,則pthread_once返回0。如果once_control參數不是PTHREAD_ONCE_INIT,那麼行為將是undefined。在LinuxThreads中:

在LinuxThreads中,實際"一次性函數"的執行狀態有三種:NEVER(0)、IN_PROGRESS(1)、DONE (2),如果once初值設為1,則由於所有pthread_once()都必須等待其中一個激發"已執行一次"信號,因此所有pthread_once ()都會陷入永久的等待中;如果設為2,則表示該函數已執行過一次,從而所有pthread_once()都會立即返回0。

這個函數在當無法編輯進程的main函數,比如寫一個庫的時候,就很有用。
TODO:如果多個線程使用的init_routine不相同怎麼辦?或者比如自己開發庫,但是user的main中已經使用不同的init_routine調用了pthread_once,那麼會是什麼結果?



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

-Advertisement-
Play Games
更多相關文章
  • 1、if語句if 語句基本用法: 其中,表達式可以是一個單純的布爾值或變數,也可以是比較表達式或邏輯表達式,如果表達式為真,則執行“語句塊”;如果表達式的值為假,就跳 過“語句塊”,繼續執行後面的語句。 2、if…else語句if…else 語句基本用法: 使用 if…else 語句時,表達式可以是 ...
  • 總體原則:命名一定要體現其在程式中的作用; Camel命名法:第一個單詞的首字母小寫,其餘每個單詞的首字母大寫;多用給變數或者欄位命名;給欄位命名必須以下劃線開始; Pascal命名法:每個單詞的首字母都大寫,其餘字母小寫;適用於方法名、類名、屬性名等; ...
  • 一. 起始 去年.NetCore2.0的發佈,公司決定新項目採用.NetCore開發,當作試驗。但是問題在於當前公司內部使用的RPC服務為Thrift v0.9 + zookeeper版本,經過個性化定製,支持了非同步,但也因為如此,這麼多年來一直沒有去升級,導致遷移工作很複雜(歷史遺留項目太多,有各 ...
  • 前言 曾有做過一個產品,有一個功能是視頻監控模塊,視頻監控首先想到的是視頻多畫面切換功能,由於前端是用WPF開發的,所以當時就做了一個多畫面切換組件,效果如下: 功能設計前提: 由於要使用海康大華天地偉業等視頻廠家的視頻,對接的方式是通過各個廠家提供的SDK(官網下載),由於播放視頻的時候需要傳遞控 ...
  • .NET 術語 1. AOT 預編譯器。與 JIT 類似,此編譯器還可將 IL 轉換為機器代碼。 與 JIT 編譯相比,AOT 編譯在應用程式執行前進行並且通常在不同電腦上執行。 由於在運行時 AOT 工具鏈不編譯,因此它們不需要最大程度地減少編譯所花費的時間。 這意味著它們可花更多的時間進行優化 ...
  • 1 .gz 1)壓縮 2)解壓縮: 註意:不能壓縮目錄!支持批量壓縮,源文件被替換成.gz結尾的文件。 2 .bz2 1)壓縮 2)解壓縮: 3)強制壓縮: 註意:不能壓縮目錄!支持批量壓縮,源文件被替換成.bz2結尾的文件。 3 .zip 1)壓縮 2)解壓縮 註意:可以壓縮目錄!支持批量壓縮,源 ...
  • VMware 安裝提示缺少MicrosoftRuntime DLL 問題解決辦法 剛剛安裝VMware失敗了試了好多辦法,在這總結一下。 下麵是程式的截圖 這是報錯信息 網上的解決方法: 當出現安裝失敗的提示時,不要將界面關閉,然後在運行視窗輸入 %temp% 然後會進如文件夾, 在文件夾里找到類似 ...
  • 1. 主機規劃 主機名稱 IP地址 操作系統 部署軟體 運行進程 備註 mini01 172.16.1.11【內網】 10.0.0.11 【外網】 CentOS 7.5 Jdk-8、zookeeper-3.4.5、Hadoop2.7.6、hbase-2.0.2、kafka_2.11-2.0.0、sp ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...