COM 經驗的八個教訓(4):單元模型對象必須保護共享數據

另一個困擾 COM 開發人員的通病是標記爲 ThreadingModel=Apartment 的進程內對象。這項指定告訴 COM,對象的實例必須只能在 STA 中創建。它還可讓 COM 自由地將這些對象實例放在任何主機進程的 STA 中。

假設客戶端應用程序有五個 STA 線程,每個線程都使用 CoCreateInstance 來創建同一個對象的一個實例。如果線程是基於 STA 的,且對象標記爲 ThreadingModel=Apartment,則這五個對象實例將在對象創建者的 STA 中創建。因爲每個對象實例都在佔用其 STA 的線程上運行,因此所有五個對象實例都可以並行運行。

到目前爲止,一切良好。現在考慮一下,如果這些對象實例共享數據會發生什麼情況。因爲對象都在併發線程上執行,兩個或更多的對象可能會同時嘗試訪問同一個數據。除非所有這些訪問都是讀取訪問,否則就會釀成災難。問題可能不會很快顯現出來;它們會以和時間緊密相關的錯誤形式出現,因此很難診斷和重現。這就解釋了以下事實的原因:ThreadingModel=Apartment 對象應該包括可同步對共享數據的訪問的代碼,除非您能夠確定對象的客戶端不會對執行訪問的方法進行重疊調用。

問題在於,太多的 COM 開發人員相信 ThreadingModel=Apartment 能夠使他們免於編寫線程安全的代碼。事實並非如此 — 至少不完全如此。ThreadingModel=Apartment 並不意味着對象必須是完全線程安全的,它代表的是一個對 COM 的承諾,即訪問兩個或更多對象實例共享的數據(或此對象和其他對象的實例共享的數據)時是以線程安全的方式進行的。而提供該線程安全性的任務應該由您,即對象實現者來負責。共享數據的類型和大小多種多樣,但大多是以全局變量、C++ 類中的靜態成員變量和函數中聲明的靜態變量的形式出現。即使是以下這樣無害的語句也會在 STA 中出問題:

因爲這個對象的所有實例都將共享一個 nCallCount 實例,編寫這些語句的正確方式如下:

注意:您可以使用臨界區、互鎖函數或您希望的任何方式,但不要忘了訪問基於 STA 的對象共享的數據時要進行同步化!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章