C++面向?qū)ο蠖嗑€程學習筆記_合作與同步

1. 競爭條件

當兩個或更多線程或進程試圖同時修改同一個共享、可修改數(shù)據(jù)塊時,這種情況稱做競爭條件或數(shù)據(jù)競爭(data race)。

區(qū)別共享可修改數(shù)據(jù)與只讀數(shù)據(jù)是重要的,區(qū)別多線程試圖修改數(shù)據(jù)塊與多線程只試圖讀取數(shù)據(jù)也是重要的。為了讓競爭條件存中,目標內(nèi)存塊必須是可修改的,而且線程必須試圖同時訪問這個塊,至少其中一個線程試圖修改這個內(nèi)存塊。

當鎖定、掛起或凍結(jié)多個線程時,相互間等待對方釋放某些資源,此時存在死鎖。競爭條件和死鎖大概是多線程處理應用的最大缺陷。多個線程和多個任務必須同步化阻止競爭條件。我們必須找到一種讓多個線程和多個任務合作使用資源避免死鎖的途徑。

同步允許多個線程或進程同時激活,同時共享資源而不干擾對方的操作。同步進程臨時序列化多個線程和任務的執(zhí)行,防止競爭條件或死鎖。序列化僅在必須對硬件或軟件資源按“一次一個”的方式訪問時才發(fā)生。太多的序列化會削弱多線程的優(yōu)勢。

2. 同步關系

共有四種基本同步關系:

SS(start-start):線程B啟動后線程A才能啟動。線程B可以在線程A啟動的同時或之前啟動,但決不能在其之后啟動;

FS(Finish-start):線程A完成一定的任務后,線程B才能啟動;

SF(start-finish):線程B完成一定的任務后,線程A才能啟動;

FF(finish-finish):線程B完成后,線程A才能完成,線程A可以在線程B之后完成,但不能在其之前完成。

3. 為了有效處理多個進程或線程間的競爭條件,有如下幾種同步機制可以使用:

a. 信號量(semaphore); b. 條件變量(conditional variable); c. 臨界區(qū)(critical section)。

信號量是一個受保護的變量,僅能被非常具體的操作來訪問。它用于幫助線程或進程同步訪問一些共享、可修改內(nèi)存塊,或者控制對一些設備的訪問。它用來充當共享資源的一種鑰匙,在某個時刻只能由一個線程或進程擁有,一旦擁有了它就可以鎖定資源進行排它性地使用,在允許資源的進一步訪問之前,其它進程或線程必須等待資源被釋放。對信號量的操作(P, V, Lock 和 Unlock)。一種信號量類型為二進制信號量(binary semaphore),只能是0或1,如mutex,在其上只有P/V兩種操作。另一種信號量是計數(shù)信號量(counting semaphore),只以非負整數(shù)值出現(xiàn)。

信號量操作具有原子性(atomicity)和不可分割性(indivisibility),它確保是不可侵害的,一旦啟動了信號量操作,就不能由于任何原因被OS搶占。

如下是幾種最常見的信號量類型:

a. 互斥信號量:幫助在代碼臨界區(qū)(初始化、鎖定請求、try塊、取消鎖定、析構(gòu))實現(xiàn)互斥的機制;

b. 事件互斥量和條件變量:用于支持線程間的廣播機制。允許一個線程向其它線程廣播通知某事件的發(fā)生。當線程鎖定某事件互斥量時,它將阻塞到接到廣播(等待、延遲、銷毀)為止;

c. 等待多個條件:與事件互斥量相同,但它擴展到包括多個事件或條件(等待、延遲、銷毀)。

在多線程編程中的第一道防線是保護程序中的臨界區(qū),將它們鎖定,不讓其它線程或進程訪問。這種保護臨界區(qū),只允許一個線程或進程在某一時刻訪問它的過程稱做互斥(mutual exclusion)。

對互斥量的初始化操作:初始化進程將分配保存信號量所需要的內(nèi)存,并賦予內(nèi)存初始值。它還決定信號量是否被占有、非占有、私有或共享。析構(gòu)操作則釋放與互斥量相關的內(nèi)存。如果此時互斥量被占有或某線程仍然等待著該互斥量,可以銷毀或關閉內(nèi)存。

互斥信號量有如下可設置屬性:

被占有或未被占有:指定設置了信號量初始狀態(tài)的一個標志。如果為被占有,請求該信號量的其它所有線程都將阻塞。一旦OS創(chuàng)建了它,調(diào)用線程將立即占有該信號量,同時可以訪問此資源。如果它沒有被任何其它線程占有,則請求此信號量任一線程馬上可以獲得占有權。

命名或匿名:命名信號量總是共享的,知道它名字的所有進程都可以使用它。當應用程序在創(chuàng)建信號量時指定一個名字則OS將為它創(chuàng)建一個帶名信號量。如果沒有指定名字,則創(chuàng)建一個匿名信號量。

私有或共享:私有信號量未命名,通過它們的句柄來識別,只能被進程內(nèi)的線程使用。共享信號量通過使用其名字或句柄可以被多進程中的線程所使用。

當存在多個線程等待著一個互斥量時,必須基于優(yōu)先權和使用的規(guī)劃方案來決定由哪一個線程占有互斥量。如果線程的優(yōu)先權不相等,則通常最高優(yōu)先權的線程第一個獲得釋放后互斥量的占有權。如果所有的線程優(yōu)先權相等,則規(guī)劃器可能使用一種輪詢或FIFO途徑來讓線程獲得互斥量的占有權。

釋放或取消鎖定互斥量不會釋放與互斥量關聯(lián)的內(nèi)存,只是放棄了對它的占有權。如果要釋放與互斥量關聯(lián)的內(nèi)存,必須銷毀或關閉互斥量。但是,如果互斥量仍然被占有,或者存在阻塞的線程還等待著該互斥量的釋放,就不能銷毀或關閉這個互斥量。

OS中的線程支持一種線程間的廣播機制,它允許一個線程廣播給另一個線程,告訴它某種事件已經(jīng)發(fā)生。這種機制使用的是所謂的條件變量或事件互斥量。當一個線程鎖定了一個事件互斥量,它將阻塞,直到它接到了一條廣播,告知它可以繼續(xù)為止。

4. 避免競爭條件

a. 不要使用可能同時位于臨界區(qū)內(nèi)的兩個進程;

b. 不要依賴對CPU的速度作出的任何假設;

c. 不要讓臨界區(qū)外部停止的進程阻塞其它進程;

d. 不要讓進程等待做任意長的時間進入其臨界區(qū)。

5. 死鎖必需的條件

a. 進程聲明排它性控制它們需求的資源;

b. 進程在等待其它資源的釋放時占有資源;

c. 資源不能強行從進程中刪除;

d. 存在一個循環(huán)等待條件。

6. 遠離死鎖

也許用于避免死鎖的最簡單技術是應用定時互斥量、事件變量、同步變量及信號量。當使用這些機制時,死鎖不能發(fā)生,因為線程或進程在繼續(xù)之前只阻塞或等待有限的時間段。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容