天天看點

《C++面向對象高效程式設計(第2版)》——2.20 什麼是多線程安全類

本節書摘來自異步社群出版社《c++面向對象高效程式設計(第2版)》一書中的第章,第2.20節,作者: 【美】kayshav dattatri,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。

c++面向對象高效程式設計(第2版)

傳統上,作業系統(os)隻支援程序(也稱為任務)。每個程序都有自己的位址空間,且有一個單獨的執行線程,程序執行一個包含一系列指令的程式。但是,現在大多數作業系統都支援單程序中的多線程。一個任務可根據需要包含多個線程,單程序中的所有線程共享程序的位址空間,線程可以通路程序中的所有全局資料。

使用線程有很多優點。首先,建立線程的成本更低(且更效率)。建立一個新程序需要涉及作業系統中的大量工作(設定記憶體頁面、注冊、程序上下文等),而線程需要的大多數資源都可以從程序中獲得。其次,線程間的通信更加容易。不同的程序位于不同的位址空間中,是以程序之間的通信(ipc)并不容易。但是,在單個程序中,不同的線程共享相同的位址空間,是以線程之間的通信非常容易。另外,在多線程中,可以阻止(block)操作,也可以并行處理操作。作業系統單獨排程(schedule)每一個線程。例如,有一個應用程式,允許從一個裝置上複制檔案到另一個裝置上,使用者可能要求在中途停止複制操作。如果建立一個僅用于等待使用者輸入的單獨程序,顯然很不合理。在這種情況下,建立一個等待使用者輸入的單獨線程更加合适。主應用程式執行複制操作,而輔助線程等待使用者輸入。如果使用者決定終止複制操作,則運作等待使用者輸入的線程,并通知主應用程式,使用者要求中止操作;然後,主應用程式線程中止複制操作。在單個應用程式中使用多線程非常普遍。再舉另外一例,文檔處理應用程式可以用一個線程列印文檔,而另一個線程執行生成索引,同時還有另一個線程用于接收使用者提供的文檔摘要資訊。

任何使用多線程的應用程式都可稱為多線程應用程式。涉及多個線程時,同步(synchronization)和互斥(mutual exclusion)尤為重要。當一個線程正在通路某段資料時(例如,列印文檔的線程),必須防止其他線程試圖通路相同的資料(為了寫入)。根據作業系統,實作可以使用互斥體(mutex)、信号量(semaphore)、臨界區(crical section)、自旋鎖定(spin-lock)、消息、事件等來達到這個目的。例如,文檔中的每一頁都由一個互斥體來保護,隻允許一個線程通路該頁。無論用何種通路控制的方案,實作必須確定不會發生死鎖(deadlock)情況。

應用程式(或系統)在多線程運作的環境中正常運作稱為多線程安全(multi-thread safe)。確定多線程安全并不容易,實作必須使用之前提及的某種同步方案來實作互斥。

我們在這裡讨論的并不是新内容,隻有在涉及多程序時,同步才是個問題。不管怎樣,一個程序不能通路另一個程序位址空間内的内容,這使得同步稍微容易一些。但是,程序中的線程共享程序所擁有的資源。是以,確定适當的同步非常重要。例如,在文檔處理的應用程式中,如果列印線程已鎖定頁面,索引線程在通路相同頁面之前必須等待,直到列印線程解鎖頁面。

在使用引用計數(reference counting)(也稱為使用計數(use count))方案的情況下,多線程安全非常關鍵。引用計數方案将在後續章節中讨論。修改引用計數必須是一個線程安全的操作。

在多線程環境中使用對象時,多線程安全更加重要。如果不能確定多線程安全,可能會導緻災難。一個程序内的兩個線程可以使用相同的對象。記住,所有對象都共享成員函數代碼。當一個線程調用一個成員函數,在成員函數内部完成執行之前,如果(作業系統)排程(schedule)另一個線程運作,且該線程也通過相同的對象調用相同的成員函數,則對象必須保證自身完整和運作良好。如果對象不能做到這一點,這樣的類就不是線程安全(thread-safe)的。當然,如果一個類(成員函數和資料成員)沒有任何線程安全的特殊要求,維持線程安全就完全不成問題。

在設計新的類時,注意多線程安全非常重要。如果類的對象即使在多線程環境下都能保持完整,必須在類的文檔中予以說明。另一方面,如果類的對象不保證多線程安全,也要在類的頭檔案和文檔中清楚地說明其局限性。不要誤認為設計的每個類都必須保證多線程安全,事實并非如此。是否需要線程安全取決于類和客戶的要求。還需記住,x類如果使用其他類作為它實作的一部分,為保證x類為線程安全,有必要保證它使用的其他類都為線程安全。或者,即使它所依賴的其他類非線程安全,至少必須保證x類線程安全(這更加困難)。為達到線程安全,下面列舉了一些指導原則:

(1)如果類聲明為線程安全,確定每個成員函數實作也是線程安全的。

(2)如果類在實作中使用其他的類(對象),確定仍然能保證線程安全。

(3)如果使用一些類庫來實作類,確定正在使用的庫函數是線程安全的。

(4)如果正在使用作業系統調用,檢查以確定這些調用都是線程安全的。

(5)當使用編譯器提供的庫時,檢查它們是否都是線程安全的。

許多庫的供應商提供輔助類,用于幫助達到線程安全。例如,檢視提供線程安全引用計數的類十分常見。如果你的項目需要線程安全,它可能會幫助實作一組確定線程安全的低級類。這樣的類可以提供引用計數、線程安全指針、線程安全列印實用程式等。如果整個項目小組都在各自的實作中使用這些類,就能保證整個項目的線程安全。

繼續閱讀