1. 程式人生 > >《Operating Systems:Three easy pieces》:對作業系統的介紹(五)

《Operating Systems:Three easy pieces》:對作業系統的介紹(五)

2.3 併發性

當在相同的記憶體空間中有許多併發執行的執行緒時,我們如何構建一個正確工作的程式?作業系統需要哪些基本型別?硬體應該提供哪些機構?我們如何使用它們來解決併發性問題?

本書另一個很重要的主題是併發性。我們使用這樣一個概念術語去描述同一個程式要同時處理多個任務時會發生的並要求解決的一些問題。併發性問題首先出現在作業系統本身中;正如上述你看到的一些虛擬化的例子,作業系統同時處理許多事情,先執行一個程序,然後另一個,等等。人們後來會發現,這麼做會引起許多影響深刻並有趣的事情。

一個多執行緒程式示例:threads.c

不幸的是,併發性問題不再僅僅侷限於作業系統本身。現代多執行緒程式也出現了同樣的問題。讓我們看看示例程式threads.c。

即使此刻你不能完全理解這個例子(我們將在後續的章節併發性中,學習這個例子的有關知識),基本思想很簡單。主程式使用pthreads_create()建立了兩個執行緒。你可以把執行緒看做與其他函式共享記憶體空間的一個函式,在他們中可以有一個以上的同時在活動中。在這個例子中,每個執行緒都在一個稱為worker()的例程中開始執行,在這個例程中,它只是在一個迴圈中增加計數器的迴圈次數。

下面是執行這個程式時發生的事情,變數迴圈的輸入值設定為1000。loops的值將決定在一個迴圈中,這兩個worker每個將增加共享計數器多少次。當我們將值設定為1000時,你預期計數器最後的值將會是多少?

你可能猜到了,當這兩個執行緒結束的時候,計數器最後的值是2000次,每個計數器增加1000次。真的,我們希望當我們輸入值為N時,我們預期輸出結果為2N,但是事實證明,生活常常不會如此簡單,我們給Loops一個更大的值,執行這個同樣的程式,看他會輸出我們的預期結果嗎?

在這個過程中,當我們輸入值為100,000時,我們得到的不是200,000的最終值,而是143,012。然後,當我們再次執行程式時,我們不僅再次得到錯誤的值,而且與上次得到的值不同。事實上,如果您用迴圈的高值一遍又一遍地執行該程式,您可能會發現有時甚至會得到正確的答案!為什麼會這樣呢?

事實證明,這些奇怪和不尋常的結果的原因與指令如何執行有關,而指令是一次執行一個。不幸的是,上面程式的一個關鍵部分,也就是增加共享計數器的部分,需要三條指令:一是將計數器的值從儲存器裝入暫存器,一個是增加1,一個是重新存進儲存器。因為這三條指令不是原子執行,所以奇怪的事情就這樣發生了。我們將在本書的第二部分詳細討論併發性問題。