併發程式設計 3 執行緒間共享資料

2022-03-14 19:57:14 字數 2597 閱讀 8181

一、共享記憶體帶來的問題

讀時沒問題,寫時會有競爭問題。

二、解決方法

1、最簡單的辦法就是對資料結構採用某種保護機制,確保只有進行修改的執行緒才能看到不變數被破壞時的中間狀態。從其他訪問執行緒的角度來看,修改不是已經完成了,就是還沒開始。

2、另乙個選擇是對資料結構和不變數的設計進行修改,修改完的結構必須能完成一系列不可分

割的變化,也就是保證每個不變數保持穩定的狀態,這就是所謂的無鎖程式設計。另一種處理條件競爭的方式是,使用事務的方式去處理資料結構的更新。(stm)

第二種是比較高階的內容,不在討論範圍內。討論第一種也就是使用互斥量保護資料

三、std::mutex 建立互斥量

1、通過呼叫成員函式lock()進行上鎖,unlock()進行解鎖。不推薦實踐中直接去呼叫成員函式,因為呼叫成員函式就意味著,必須記住在每個函式出口都要去呼叫unlock(。c++標準庫為互斥量提供了乙個raii語法的模板類--自解鎖std::lock_guard ,在其作用域內:其會在構造的時候提供已鎖的互斥量,並在析構的時候進行解鎖,從而保證了乙個已鎖的互斥量總是會被正確的解鎖。      #include 標頭檔案  

lock_guard 介紹

2、條件競爭---保護資料

使用互斥量來保護資料,並不是僅僅在每乙個成員函式中都加入乙個 std::lock_guard 物件那

麼簡單;乙個迷失的指標或引用,將會讓這種保護形同虛設。不過,檢查迷失指標或引用是

很容易的,只要1)沒有成員函式通過返回值或者輸出引數的形式向其呼叫者返回指向受保護數

據的指標或引用,2)成員函式沒有通過指標或引用的方式來呼叫外部沒有被保護的函式或者變數,資料就是安全的。

class

some_data

;class

};some_data*unprotected;

void malicious_function(some_data&protected_data)

//相當於在保護機制下,函式訪問了乙個沒有被保護的外部函式.也就破壞了保護了

//意味著foo能夠繞過保護機制將函式 malicious_function 傳遞進func()!

3、函式介面處的競爭

stacks;

if (! s.empty())

兩個執行緒在執行上面同一段**時,可能會交錯的執行2、3.如此帶來的競爭會訪問到不同的值。雖然對裡面資料進行了加鎖,但是介面沒有加鎖。一種實現方式是用同乙個鎖,鎖住2和3,但是得手動呼叫成員函式鎖,不能用自解鎖。

而且,可能會在2之後又異常丟擲,無法捕獲。(很多情況太複雜)可以將top()和pop()封裝到乙個函式裡面去,用鎖保護呼叫。若傳參不方便也可過載pop(),裡面包含top()。

4、死鎖   

乙個給定操作被兩個或兩個以上的互斥量競爭時:死鎖。與條件競爭完全相反——不同的兩個執行緒會互相等待,從而什麼都沒做。std::lock ——可以一次性鎖住多個(兩個以上)的互斥量,並且沒有***(死鎖風險)。

避免巢狀鎖;避免在持有鎖時呼叫使用者提供的**  ;使用固定順序獲取鎖

產生死鎖的四個必要條件:

(1) 互斥條件:乙個資源每次只能被乙個程序使用。

(2) 請求與保持條件:乙個程序因請求資源而阻塞時,對已獲得的資源保持不放。

(3) 不剝奪條件:程序已獲得的資源,在末使用完之前,不能強行剝奪(有的可以情形剝奪的,              比如記憶體搶占)。

(4) 迴圈等待條件:若干程序之間形成一種頭尾相接的迴圈等待資源關係,資源之間相互依賴。

避免死鎖就是從破壞上面四個必要條件的角度思考。

由於資源互斥是資源使用的固有特性是無法改變的。

破壞「不可剝奪」條件:乙個程序不能獲得所需要的全部資源時便處於等待狀態,等待期間他占有的資源將被隱式的釋放重新加入到 系統的資源列表中,可以被其他的程序使用,而等待的程序只有重新獲得自己原有的資源以及新申請的資源才可以重新啟動,執行。

破壞」請求與保持條件「:第一種方法靜態分配即每個程序在開始執行時就申請他所需要的全部資源,執行結束再**資源。第二種是動態分配即每個程序在申請所需要的資源時,暫時放棄占用系統資源,若再需要就嘗試在一次重新分配所有資源。

破壞「迴圈等待」條件:採用資源有序分配其基本思想是將系統中的所有資源順序編號,將緊缺的,稀少的採用較大的編號,在申請資源時必須按照編號的順序進行,乙個程序只有獲得較小編號的程序才能申請較大編號的程序(不一定連續編號請求)。這樣就不能形成環了,較大編號的不可能請求到小編號的資源。

5、巢狀鎖:(不是很理解)

當乙個執行緒已經獲取乙個 std::mutex 時(已經上鎖),並對其再次上鎖,這個操作就是錯誤

的。然而,在某些情況下,乙個執行緒嘗試獲取同乙個互斥量多次,而沒有對其進行一次釋放是可以的,庫提供了 std::recursive_mutex 類。

有時成員函式會呼叫另乙個成員函式,這種情況下,第二個成員函式也會試圖鎖住互斥量,這就會導致未定義行為的發生。「變通的」解決方案會將互斥量轉為巢狀鎖,第二個成員函式就能成功的進行上鎖,並且函式能繼續執行。這種也不好,不如將第二個函式加到類的私有成員,讓其他成員函式對其呼叫。(盡量實現原子操作)

java併發 多個執行緒間共享資料

先看乙個多執行緒間共享資料的問題 設計四個執行緒,其中兩個執行緒每次對data增加1,另外兩個執行緒每次對data減少1。從問題來看,很明顯涉及到了執行緒間通資料的共享,四個執行緒共享乙個data,共同操作乙個data。我們先把上面這個問題放在一邊,慢慢分析多個執行緒之間共享資料的一些情況,從最簡單...

執行緒間共享資料

首先給大家分享乙個巨牛巨牛的人工智慧教程,是我無意中發現的。教程不僅零基礎,通俗易懂,而且非常風趣幽默,還時不時有內涵段子,像看 一樣,哈哈 我正在學習中,覺得太牛了,所以分享給大家!點這裡可以跳轉到教程 一 每個執行緒執行的 相同 若每個執行緒執行的 相同,共享資料就比較方便。可以使用同乙個run...

Java併發程式設計學習 3 執行緒間協作

目錄頁 t.join 方法阻塞呼叫此方法的執行緒 calling thread 直到執行緒t完成,此執行緒再繼續 直接上 package com.concurrent.coline.part3.join 類說明 join方法的使用 public class usejoin override publ...