多線程:如何使用MFC同步處理類別
在寫入多線程應用程式時,同步處理線程之間的資源存取是常見的問題。 讓兩個以上的線程同時存取相同的數據,可能會導致非預期且無法預測的結果。 例如,一個線程可能會更新結構的內容,而另一個線程正在讀取相同結構的內容。 讀取線程將接收的數據未知:舊數據、新寫入的數據,或兩者可能混合。 MFC 提供一些同步處理和同步處理存取類別,以協助解決此問題。 本主題說明可用的類別,以及如何使用這些類別在一般多線程應用程式中建立安全線程類別。
典型的多線程應用程式有一個類別,代表在線程之間共用的資源。 設計正確、完全安全線程的類別不需要您呼叫任何同步處理函式。 所有項目都會在類別內部處理,讓您專注於如何最好地使用 類別,而不是它可能如何損毀。 建立完全安全線程類別的有效技巧是將同步處理類別合併到資源類別。 將同步處理類別合併到共用類別是一個直接的程式。
例如,採用維護帳戶連結清單的應用程式。 此應用程式允許在個別視窗中檢查最多三個帳戶,但在任何特定時間只能更新一個帳戶。 更新帳戶時,會透過網路將更新的數據傳送至數據封存。
這個範例應用程式會使用這三種類型的同步處理類別。 因為它允許一次檢查最多三個帳戶,所以它會使用 CSemaphore 來限制對三個檢視物件的存取。 嘗試檢視第四個帳戶時,應用程式會等到前三個視窗的其中一個關閉或失敗。 更新帳戶時,應用程式會使用 CCriticalSection 來確保一次只會更新一個帳戶。 更新成功之後,它會發出 CEvent 的訊號,這會釋放等候事件發出訊號的線程。 此線程會將新數據傳送至數據封存。
設計安全線程類別
若要讓類別完全安全線程,請先將適當的同步處理類別新增至共用類別做為數據成員。 在先前的帳戶管理範例中, CSemaphore
數據成員會新增至檢視類別、 CCriticalSection
數據成員會新增至連結清單類別,並將 CEvent
數據成員新增至數據記憶體類別。
接下來,將同步處理呼叫新增至修改 類別中數據或存取受控制資源的所有成員函式。 在每個函式中,您應該建立 CSingleLock 或 CMultiLock 物件,並呼叫該物件的Lock
函式。 當鎖定物件超出範圍並終結時,物件的解構函式會為您呼叫 Unlock
,釋放資源。 當然,如有需要,您可以直接呼叫 Unlock
。
以這種方式設計線程安全類別可讓您輕鬆地在多線程應用程式中使用,就像非線程安全類別一樣,但具有較高層級的安全性。 將同步處理物件和同步存取物件封裝到資源的 類別,可提供完全安全線程程序設計的所有優點,而不需要維護同步處理程式碼的缺點。
下列程式代碼範例示範這個方法,方法是使用在共用資源類別和 CSingleLock
物件中宣告的數據成員 m_CritSection
(屬於 類型CCriticalSection
)。 使用 物件的位址建立 CSingleLock
對象,嘗試同步處理共用資源(衍生自 CWinThread
)。m_CritSection
嘗試鎖定資源,並在取得時在共享物件上完成工作。 當工作完成時,資源會解除鎖定, Unlock
並呼叫 。
CSingleLock singleLock(&m_CritSection);
singleLock.Lock();
// resource locked
//.usage of shared resource...
singleLock.Unlock();
注意
CCriticalSection
不同於其他 MFC 同步處理類別,沒有計時鎖定要求的選項。 等候線程變成可用期間是無限的。
此方法的缺點是,類別會比相同類別稍微慢一點,而不會新增同步處理物件。 此外,如果可能會有一個以上的線程可能會刪除物件,合併的方法可能不一定能夠運作。 在此情況下,最好維護個別的同步處理物件。
如需判斷在不同情況下要使用的同步處理類別的相關信息,請參閱 多線程:何時使用同步處理類別。 如需同步處理的詳細資訊,請參閱 Windows SDK 中的同步 處理。 如需 MFC 中多線程支援的詳細資訊,請參閱 使用 C++ 和 MFC 進行多線程處理。