1. 程式人生 > >作業系統核心原理-4.執行緒原理(下):死鎖基礎原理

作業系統核心原理-4.執行緒原理(下):死鎖基礎原理

  我們都見過交通阻塞,一大堆汽車因為爭奪行路權,互不相讓而造成阻塞,又或者因為車輛發生故障拋錨或兩輛車相撞而造成道路阻塞。在這種情況下,所有的車都停下來,誰也無法前行,這就是死鎖。本篇就來了解一下什麼是死鎖,如何應對死鎖。

一、死鎖初窺

1.1 為何會發生死鎖?

  死鎖的發生歸根結底是因為對資源的競爭。因為大家都想要某種資源,但又不能隨心所欲地得到所有資源,在爭奪的僵局中,導致任何人無法繼續推進。

  在一個系統裡存在多個執行緒,而這些執行緒共享該計算機系統裡的資源。因為資源競爭而造成系統無法繼續推進就難以避免了。這裡的資源可以使硬體(CPU、記憶體、磁碟等),也可以是軟體(例如鎖、訊號量等)。

1.2 死鎖的定義與必要條件

  (1)死鎖的定義

  如果有一組執行緒,每個執行緒都在等待一個事件的發生,而這個事件只能有該執行緒裡面的另一執行緒發出,則稱這組執行緒發生了死鎖。這裡的事件主要是資源的釋放,在死鎖狀態中,沒有執行緒可以執行、釋放資源或被叫醒。

  例如,有執行緒A和執行緒B如下:

  如果執行緒A和執行緒B交替執行,那麼執行緒A和執行緒B均會因為無法獲得對應的資源而無法繼續執行也不能釋放鎖,從而造成死鎖,如下圖所示:

  (2)死鎖的4個必要條件

  ① 資源有限:即一個系統裡面的資源數量是有限的,以致於無法同時滿足所有執行緒的資源需求。

  ② 持有等待:即一個執行緒在請求新的資源時,其已經獲得的資源並不釋放,而是繼續持有。

  ③ 不可搶佔:即如果可以搶佔一個資源,則也不會發生死鎖。(凡是可以搶佔的資源,均不會稱為死鎖的原因)

  ④ 迴圈等待:即如果你等我、我等你,大家都這樣等著對方,就產生了死鎖。

二、應對死鎖

2.1 引子:哲學家就餐問題

  哲學家每天只做兩件事:思考和吃飯。他們每天不停地思考人生的這裡,比如人從什麼地方來,人為什麼是現在這個樣子,人類往哪裡去等深刻的問題。當然,思考久了就會感到飢餓,而飢餓了就要吃飯。但是,吃飯是有規矩的:

  哲學家圍坐在一個圓桌邊,每個人的左右兩邊均放著一根筷子。如果要吃飯,需要獲得左右兩邊的筷子(不能用一根筷子吃飯),如下圖所示:

  我們很自然地得到一個演算法,對於每一個哲學家,執行以下的演算法:

  ① 等待左邊的筷子可用,然後拿起左邊的筷子。

  ② 等待右邊的筷子可用,然後拿起右邊的筷子。

  ③ 吃飯。

  ④ 放下兩根筷子。

  顯然,如果每個哲學家穿插著執行,將會出現每個哲學家都拿起左邊筷子,而等待右邊筷子的情況,即死鎖將會發生。那麼,有木有辦法防止哲學家出現死鎖呢?

2.2 死鎖的應對方法

  作業系統應對死鎖的策略可以分為兩大種、四小種。兩大種是:允許死鎖發生 和 不讓死鎖發生。四小種是:允許死鎖發生有兩個子對策,一是假裝看不見不予理睬,二是死鎖發生後想辦法解決;不讓死鎖發生也有兩個子對策,一是通過平時的仔細檢點避免難題出現,二是通過將發生死鎖的必要條件消除杜絕死鎖的發生。

  (1)順其自然不予理睬

  此種策略就是作業系統不做任何措施,任由死鎖發生。老子曾說,無為而治,說的就是這個策略。但是,如果牽涉到高可靠性系統、實時控制系統,那就另當別論了,那絕對不允許死鎖。

  (2)死鎖的檢測與恢復

  在死鎖檢測上,一般會利用到兩個矩陣:一個是資源分配矩陣,另一個是資源等待矩陣,如下圖所示:

資源分配矩陣

資源等待矩陣

  此外,還維持兩個向量:一個是系統資源總量向量(表示系統中所有資源的總數是多少),另一個是系統當前可用資源向量(代表系統現在還有多少可用的資源),如下圖所示:

  有了上面的矩陣和向量,我們就可以通過簡單地矩陣運算來判斷系統是否發生了死鎖。例如,將上圖中的資源可用數量矩陣與資源等待矩陣的每一行相減,都會出現負值,那麼該系統將要發生死鎖。

  在死鎖恢復上,首先可以搶佔(即將某個執行緒所佔有的資源強行拿走,分配給別的執行緒),其次可以將整個執行緒Kill殺掉(因為搶佔一個執行緒的資源有可能造成該執行緒無法再正確運行了),最後則是Rollback回滾(即將整個系統回滾到過去的某個狀態,大家從那個狀態重新來過)

  (3)死鎖的動態避免

  死鎖的檢測與恢復屬於後發制人,這時死鎖的消極後果已經產生,即使修復也已經浪費了時間,降低了效率,甚至造成了其他損失。因此,需要更加積極主動一點,不要等到死鎖發生了再亡羊補牢,而是在執行中就小心翼翼,不讓思索發生。

  動態避免的原則在於:在每次進行資源分配時,必須經過仔細計算,確保該資源請求批准後系統不會進入死鎖或潛在的死鎖狀態。例如,有一種資源的數量為10個,當前有3個執行緒正在執行。每個執行緒需要資源的最大數和當前已經佔用的資源數如下表所示:

  可以通過以下分配過程得知,存在一個資源分配順序使得所有執行緒都能獲得其需要的資源,從而得知當前狀態是安全狀態,不會產生死鎖。相反,如果不存在這樣一個順序,那麼就有可能產生死鎖。

  動態避免的優點就是無需等待死鎖的發生,而是在死鎖有可能發生的時候採取先發制人的措施,斷然拒絕有可能進入死鎖的資源請求。但是,計算一個狀態是否安全並不是一件容易的事情。

  (4)死鎖的靜態防止

  該策略的中心思想是:清除死鎖發生的土壤(即死鎖的4個必要條件),只要清除4個必要條件中的一個,那麼死鎖將無法發生。

  ① 清除資源獨佔條件:一是增加資源到所有執行緒滿足的資源需要,但這並不實際,因為資源是有限的;二是將資源變為共享,但並不適合與所有的資源(例如鍵盤輸入就無法共享)。

  ② 清除保持和請求條件:一個執行緒必須一次請求其所需的所有資源,而不是一般情況下的請求一點資源做一點事情。由於一個執行緒一次就獲得了其所需的所有資源,該執行緒就可以順利執行,不會發生死鎖。

  ③ 清除非搶佔條件:允許搶佔資源,也就是說可以從一個執行緒手上將資源搶奪過來。

  ④ 清除迴圈等待條件:出現迴圈等待是因為執行緒請求資源的順序是隨機的,所以只要約定執行緒對資源的使用順序,那麼死鎖就不能發生。

2.3 銀行家演算法

  顧名思義,銀行家演算法就是仿照銀行發放貸款時採用的控制方式而設計的一種死鎖避免演算法,該演算法的策略是實現動態避免死鎖

  銀行家演算法的基本思想是:分配資源之前,判斷系統是否是安全的;若是,才分配。每分配一次資源就測試一次是否安全,不是資源全部就位後才測試。我們可以把作業系統看作是銀行家,作業系統管理的資源相當於銀行家管理的資金,程序向作業系統請求分配資源相當於使用者向銀行家貸款。概括起來基本思想就是:

   ① 分配檢測:Request < Need

                        Request < Available

      ② 安全序列檢測演算法

  下面看一個在作業系統教科書中出現的例子:

  某系統有R1,R2,R3共3中資源,在T0時刻P0,P1,P2,P3和P4這5個程序對資源的佔用和需求情況如下表1,此時系統的可用資源向量為(3,3,2)。試問:

  1、T0時刻系統是否存在安全序列?

  2、P1請求資源:P1發出請求向量Request(1,0,2),系統是否接受該請求?請使用銀行家演算法檢查

  表1 T0時刻的資源分配表

MAX Allocation Need Available
P0            7 5 3         0 1 0         7 4 3          3 3 2        
P1 3 2 2     2 0 0 1 2 2     
P2 9 0 2 3 0 2 6 0 0
P3 2 2 2  2 1 1   0 1 1
P4 4 3 3 0 0 2 4 3 1

  1、T0時刻系統是否存在安全序列?

    Available > Need1 ----> 可用資源分配給P1,直到P1程序執行完成,然後Available = Available + Allocation1 = (5,3,2)

           Available > Need3 ----> 可用資源分配給P3,直到P3程序執行完成,然後Available = Available + Allocation3 = (7,4,3)

    Available > Need4 依次類推

        得到安全序列為:P1,P3,P4,P2,P0

  2、P1請求資源:P1發出請求向量Request(1,0,2),系統是否接受該請求?請使用銀行家演算法檢查

   第一步(假分配檢查):把Request分配給P1,必須滿足Request要小於Available,Request要小於Need。

                       Request(1,0,2)< Available(3,3,2)

                       Request(1,0,2)< Need(1,2,2)

        因為滿足第一步檢查,進入第二層檢查(安全序列檢查)。

   第二步(安全序列檢查):建立安全性檢查表

Work   Need Allocation   Work+Allocation   Finish
P1 2 3 0 0 2 0 3 0 2

  如果 Work > Need,那麼執行Work+Allocation,得到:                

Work   Need Allocation   Work+Allocation   Finish
P1   2 3 0 0 2 0     3 0 2  5 3 2  true
5 3 2    

   找到Need < Work的程序,如果沒有找到這樣的程序而程序集合沒有執行,則演算法返回,得到不存在安全序列結果,否則繼續執行該演算法。

   這裡我們找到了P3程序。修改安全序列檢查表:

Work   Need Allocation   Work+Allocation   Finish
P1   2 3 0 0 2 0     3 0 2  5 3 2  true
P3 5 3 2     0 1 1   2 1 1  7 4 3  true
7 4 3

  這樣一直執行到所有的程序到完成,以完成該安全序列檢查表:

Work   Need Allocation   Work+Allocation   Finish
P1   2 3 0 0 2 0     3 0 2  5 3 2  true
P3 5 3 2     0 1 1 2 1 1  7 4 3  true
P4 7 4 3 4 3 1 0 0 2  7 4 5  true
P0 7 4 5 7 4 3 0 1 0  7 5 5  true
P2 7 5 5 6 0 0 3 0 2 10 5 7  true

      這樣就找到了整個安全序列為:P1,P3,P4,P0,P2

  總的來說,銀行家演算法是一個動態避免死鎖演算法,通過對資源的仔細分配以避免死鎖。其特點是可以超額批准客戶的信用額度,即所有客戶的信用額度之和可以超過銀行的全部資本,這就是槓桿(Leverage)

2.4 解決:哲學家就餐問題

  這裡使用C#語言,模擬訊號量,以消除死鎖的必要條件(消除保持並等待的必要條件)的方式來實現解決哲學家就餐問題。

  (1)首先定義哲學家的三種狀態:

    /// <summary>
    /// 哲學家狀態
    /// </summary>
    public enum StateEnum
    {
        // 思考狀態
        THINKING = 0,
        // 飢餓狀態
        HUNGRY = 1,
        // 吃飯狀態
        EATING = 2
    }

  (2)然後定義一個臨界區互斥用的訊號量,給每個哲學家單獨定義一個訊號量。如果一個哲學家需要阻塞,則阻塞發生在該訊號量上。

    private const int NumOfPhilosopher = 5; // 哲學家人數
    private static StateEnum[] states = new StateEnum[NumOfPhilosopher]; // 記錄每個哲學家當前狀態的陣列
    private static semaphore mutex = 1; // 模擬互斥訊號量
    private static semaphore[] s = new semaphore[NumOfPhilosopher]; // 每個哲學家等待一個單獨的訊號量

  這裡的semaphore其實就是int型別:

  using semaphore = System.Int32;

  要模擬互斥訊號量,需要有兩種基本原語操作Up 和 Down:

    /// <summary>
    /// 互斥訊號量Down
    /// </summary>
    private static void Down(semaphore mutex)
    {
        if (mutex == 1)
        {
            mutex--;
        }
    }

    /// <summary>
    /// 互斥訊號量Down
    /// </summary>
    private static void Down(ref semaphore mutex)
    {
        // 阻塞操作
        while (mutex < 1) { }
    }

    /// <summary>
    /// 互斥訊號量Up
    /// </summary>
    private static void Up(semaphore mutex)
    {
        if (mutex == 0)
        {
            mutex++;
        }
    }

    /// <summary>
    /// 互斥訊號量Up
    /// </summary>
    private static void Up(ref semaphore mutex)
    {
        if (mutex == 0)
        {
            mutex++;
        }
    }

  (3)哲學家的兩種生活狀態:Think 和 Eat

    /// <summary>
    /// 思考
    /// </summary>
    /// <param name="philosoper">哲學家編號</param>
    private static void Think(int philosopher)
    {
        Console.WriteLine("Philosopher:{0} IS THINKING.", philosopher + 1);
        System.Diagnostics.Debug.WriteLine("Philosopher:{0} IS THINKING.", philosopher + 1);
    }

    /// <summary>
    /// 吃飯
    /// </summary>
    /// <param name="philosoper">哲學家編號</param>
    private static void Eat(int philosopher)
    {
        Console.WriteLine("Philosopher:{0} IS EATING.", philosopher + 1);
        System.Diagnostics.Debug.WriteLine("Philosopher:{0} IS EATING.", philosopher + 1);
    }

  (4)哲學家的日常生活:思考,拿筷子,吃飯,放下筷子,繼續思考......

    /// <summary>
    /// 哲學家程式
    /// </summary>
    /// <param name="philosopher">哲學家編號</param>
    private static void PhilosopherRoutine(object number)
    {
        int philosopher = (semaphore)number;
        while (true)
        {
            Think(philosopher);
            TakeChopsticks(philosopher);    // 同時獲得兩根筷子,否則阻塞等待
            Eat(philosopher);
            PutChopsticks(philosopher);     // 同時放下兩根筷子
        }
    }

    /// <summary>
    /// 獲取筷子
    /// </summary>
    /// <param name="philosoper">哲學家編號</param>
    private static void TakeChopsticks(int philosopher)
    {
        Down(mutex);

        states[philosopher] = StateEnum.HUNGRY;
        Test(philosopher);  // 試圖拿起兩根筷子
        Up(mutex);

        Down(ref s[philosopher]);    // 如果沒有拿到筷子,則繼續阻塞等待
    }

    /// <summary>
    /// 放下筷子
    /// </summary>
    /// <param name="philosoper">哲學家編號</param>
    private static void PutChopsticks(int philosopher)
    {
        Down(mutex);

        states[philosopher] = StateEnum.THINKING;
        int left = (philosopher + NumOfPhilosopher - 1) % NumOfPhilosopher;
        int right = (philosopher + 1) % NumOfPhilosopher;
        // 測試左面的哲學家是否可以吃飯
        Test(left);
        // 測試右面的哲學家是否可以吃飯
        Test(right);

        Up(mutex);
    }

    /// <summary>
    /// 測試是否可以同時拿起兩根筷子
    /// </summary>
    /// <param name="philosopher">哲學家編號</param>
    private static void Test(int philosopher)
    {
        int left = (philosopher + NumOfPhilosopher - 1) % NumOfPhilosopher;
        int right = (philosopher + 1) % NumOfPhilosopher;

        if (states[philosopher] == StateEnum.HUNGRY && states[left] != StateEnum.EATING &&
            states[right] != StateEnum.EATING)
        {
            // 可以拿起兩根筷子,改變哲學家狀態到吃飯狀態
            states[philosopher] = StateEnum.EATING;
            // 發出叫醒訊號
            Up(ref s[philosopher]);
        }
    }

  Run之後的結果如下圖所示:

  這樣看不清楚,截一段結果出來看看:

Philosopher:2 IS THINKING.
Philosopher:3 IS THINKING.
Philosopher:5 IS THINKING.
Philosopher:1 IS THINKING.
Philosopher:5 IS EATING.
Philosopher:4 IS THINKING.
Philosopher:3 IS EATING.
Philosopher:5 IS THINKING.
Philosopher:2 IS EATING.
Philosopher:1 IS EATING.
Philosopher:4 IS EATING.
Philosopher:5 IS EATING.
Philosopher:3 IS THINKING.
Philosopher:1 IS THINKING.
Philosopher:4 IS THINKING.
Philosopher:5 IS THINKING.
Philosopher:3 IS EATING.
Philosopher:1 IS EATING.
Philosopher:2 IS THINKING.
Philosopher:4 IS EATING.
Philosopher:5 IS EATING.
Philosopher:3 IS THINKING.
Philosopher:5 IS THINKING.
Philosopher:4 IS THINKING.
Philosopher:2 IS EATING.
Philosopher:1 IS THINKING.
Philosopher:3 IS EATING.
Philosopher:4 IS EATING.
Philosopher:2 IS THINKING.
Philosopher:4 IS THINKING.
Philosopher:2 IS EATING.
Philosopher:4 IS EATING.
Philosopher:5 IS EATING.
Philosopher:3 IS THINKING.
Philosopher:5 IS THINKING.
Philosopher:4 IS THINKING.
Philosopher:2 IS THINKING.
Philosopher:3 IS EATING.
Philosopher:5 IS EATING.
Philosopher:4 IS EATING.
Philosopher:2 IS EATING.
Philosopher:1 IS EATING.
Philosopher:5 IS THINKING.
Philosopher:3 IS THINKING.
Philosopher:5 IS EATING.
Philosopher:3 IS EATING.
Philosopher:4 IS THINKING.
Philosopher:2 IS THINKING.
Philosopher:4 IS EATING.
Philosopher:5 IS THINKING.
Philosopher:3 IS THINKING.
Philosopher:4 IS THINKING.
Philosopher:2 IS EATING.
Philosopher:5 IS EATING.
Philosopher:4 IS EATING.
Philosopher:2 IS THINKING.
Philosopher:5 IS THINKING.
Philosopher:3 IS EATING.
Philosopher:5 IS EATING.
Philosopher:1 IS THINKING.
Philosopher:4 IS THINKING.
Philosopher:2 IS EATING.
Philosopher:5 IS THINKING.
Philosopher:4 IS EATING.
Philosopher:2 IS THINKING.
Philosopher:5 IS EATING.
Philosopher:3 IS THINKING.
Philosopher:5 IS THINKING.
Philosopher:1 IS EATING.
Philosopher:5 IS EATING.
Philosopher:4 IS THINKING.
Philosopher:2 IS EATING.
Philosopher:4 IS EATING.
Philosopher:5 IS THINKING.
Philosopher:3 IS EATING.
Philosopher:4 IS THINKING.
Philosopher:2 IS THINKING.
Philosopher:5 IS EATING.
Philosopher:3 IS THINKING.
Philosopher:5 IS THINKING.
Philosopher:3 IS EATING.
Philosopher:4 IS EATING.
Philosopher:2 IS EATING.
Philosopher:5 IS EATING.
Philosopher:3 IS THINKING.
Philosopher:5 IS THINKING.
Philosopher:1 IS THINKING.
Philosopher:4 IS THINKING.
Philosopher:2 IS THINKING.
Philosopher:5 IS EATING.
Philosopher:3 IS EATING.
Philosopher:5 IS THINKING.
Philosopher:4 IS EATING.
Philosopher:3 IS THINKING.
Philosopher:1 IS EATING.
Philosopher:4 IS THINKING.
Philosopher:2 IS EATING.
Philosopher:5 IS EATING.
Philosopher:3 IS EATING.
Philosopher:1 IS THINKING.
Philosopher:4 IS EATING.
Philosopher:2 IS THINKING.
Philosopher:5 IS THINKING.
Philosopher:5 IS EATING.

  可以看到,哲學家們交替著思考吃飯,井然有序,沒有發生死鎖。這裡沒有使用.NET中現有的Mutex、Semaphore等型別,而採用了一個int型別來模擬最簡單的互斥訊號量。

  完整的程式碼如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using semaphore = System.Int32;

namespace PhilosopherDemo
{
    public class Program
    {
        private const int NumOfPhilosopher = 5; // 哲學家人數
        private static StateEnum[] states = new StateEnum[NumOfPhilosopher]; // 記錄每個哲學家當前狀態的陣列
        private static semaphore mutex = 1; // 模擬互斥訊號量
        private static semaphore[] s = new semaphore[NumOfPhilosopher]; // 每個哲學家等待一個單獨的訊號量

        /// <summary>
        /// 初始化哲學家狀態
        /// </summary>
        private static void InitializePhilosopher()
        {
            for (int i = 0; i < NumOfPhilosopher; i++)
            {
                states[i] = StateEnum.THINKING;
                s[i] = 1;
            }
        }

        /// <summary>
        /// 哲學家程式
        /// </summary>
        /// <param name="philosopher">哲學家編號</param>
        private static void PhilosopherRoutine(object number)
        {
            int philosopher = (semaphore)number;
            while (true)
            {
                Think(philosopher);
                TakeChopsticks(philosopher);    // 同時獲得兩根筷子,否則阻塞等待
                Eat(philosopher);
                PutChopsticks(philosopher);     // 同時放下兩根筷子
            }
        }

        /// <summary>
        /// 獲取筷子
        /// </summary>
        /// <param name="philosoper">哲學家編號</param>
        private static void TakeChopsticks(int philosopher)
        {
            Down(mutex);

            states[philosopher] = StateEnum.HUNGRY;
            Test(philosopher);  // 試圖拿起兩根筷子
            Up(mutex);

            Down(ref s[philosopher]);    // 如果沒有拿到筷子,則繼續阻塞等待
        }

        /// <summary>
        /// 放下筷子
        /// </summary>
        /// <param name="philosoper">哲學家編號</param>
        private static void PutChopsticks(int philosopher)
        {
            Down(mutex);

            states[philosopher] = StateEnum.THINKING;
            int left = (philosopher + NumOfPhilosopher - 1) % NumOfPhilosopher;
            int right = (philosopher + 1) % NumOfPhilosopher;
            // 測試左面的哲學家是否可以吃飯
            Test(left);
            // 測試右面的哲學家是否可以吃飯
            Test(right);

            Up(mutex);
        }

        /// <summary>
        /// 測試是否可以同時拿起兩根筷子
        /// </summary>
        /// <param name="philosopher">哲學家編號</param>
        private static void Test(int philosopher)
        {
            int left = (philosopher + NumOfPhilosopher - 1) % NumOfPhilosopher;
            int right = (philosopher + 1) % NumOfPhilosopher;

            if (states[philosopher] == StateEnum.HUNGRY && states[left] != StateEnum.EATING &&
                states[right] != StateEnum.EATING)
            {
                // 可以拿起兩根筷子,改變哲學家狀態到吃飯狀態
                states[philosopher] = StateEnum.EATING;
                // 發出叫醒訊號
                Up(ref s[philosopher]);
            }
        }

        /// <summary>
        /// 思考
        /// </summary>
        /// <param name="philosoper">哲學家編號</param>
        private static void Think(int philosopher)
        {
            Console.WriteLine("Philosopher:{0} IS THINKING.", philosopher + 1);
            System.Diagnostics.Debug.WriteLine("Philosopher:{0} IS THINKING.", philosopher + 1);
        }

        /// <summary>
        /// 吃飯
        /// </summary>
        /// <param name="philosoper">哲學家編號</param>
        private static void Eat(int philosopher)
        {
            Console.WriteLine("Philosopher:{0} IS EATING.", philosopher + 1);
            System.Diagnostics.Debug.WriteLine("Philosopher:{0} IS EATING.", philosopher + 1);
        }

        /// <summary>
        /// 互斥訊號量Down
        /// </summary>
        private static void Down(semaphore mutex)
        {
            if (mutex == 1)
            {
                mutex--;
            }
        }

        /// <summary>
        /// 互斥訊號量Down
        /// </summary>
        private static void Down(ref semaphore mutex)
        {
            // 阻塞操作
            while (mutex < 1) { }
        }

        /// <summary>
        /// 互斥訊號量Up
        /// </summary>
        private static void Up(semaphore mutex)
        {
            if (mutex == 0)
            {
                mutex++;
            }
        }

        /// <summary>
        /// 互斥訊號量Up
        /// </summary>
        private static void Up(ref semaphore mutex)
        {
            if (mutex == 0)
            {
                mutex++;
            }
        }

        public static void Main(string[] args)
        {
            InitializePhilosopher();

            for (int i = 0; i < NumOfPhilosopher; i++)
            {
                Thread thread = new Thread(PhilosopherRoutine);
                thread.Start(i);
            }

            Console.ReadKey();
        }
    }
}
View Code

參考資料

鄒恆明,《作業系統之哲學原理》,機械工業出版社

作者:周旭龍

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連結。

相關推薦

作業系統核心原理-4.執行原理基礎原理

  我們都見過交通阻塞,一大堆汽車因為爭奪行路權,互不相讓而造成阻塞,又或者因為車輛發生故障拋錨或兩輛車相撞而造成道路阻塞。在這種情況下,所有的車都停下來,誰也無法前行,這就是死鎖。本篇就來了解一下什麼是死鎖,如何應對死鎖。 一、死鎖初窺 1.1 為何會發生死鎖?   死鎖的發生歸根結底是因為對資源的競

執行學習5synchronized 的基礎使用

2018年10月03日 目錄 前言 前言 java中已經有了內建鎖:synchronized,synchronized的特點是使用簡單,一切交給JVM去處理,不需要顯示釋放; j

Java多執行學習AQS 原理以及 AQS 同步元件總結

常見問題:AQS 原理?;CountDownLatch和CyclicBarrier瞭解嗎,兩者的區別是什麼?用過Semaphore嗎? 本節思維導圖: 阿里雲產品 1888 代金券領取:https://promotion.aliyun.com/ntms

java多執行系列Thread、Runnable、Callable實現多執行的區別

實現多執行緒 java實現多執行緒的方法有三種,分別是繼承thread類,實現runnable介面,實現callable介面(call方法有返回值) /** * 繼承Thread */ public class MyThread extends Thread{ int a = 0;

執行學習執行的互斥

生產者與消費者模型 在講同步和互斥之前,首先了解一下消費者模型 什麼是消費者模型? 消費者模型是一個描述消費者和生產者之間的關係的一個模型,生產者和消費者模型指的是在一個場所中,兩個角色,三種關係 消費者和消費者之間——互斥 消費者之間是競爭關係,比如有一個

C++ 多執行框架 2Mutex 互斥和 Sem 訊號量

互斥和訊號量是多執行緒程式設計的兩個基礎,其原理就不詳細說了,大家去看看作業系統的書或者網上查查吧。 對於互斥的實現,無論什麼作業系統都離不開三個步驟 1.初始化互斥鎖 2.鎖操作 3.解鎖操作 對於不同的系統只是實現的函式有一些不同而已,但是功能其實都大同小異,在

C++ 多執行框架1new 一下就啟動一個執行

幾年前寫過一個C++的多執行緒框架,雖然寫完了,但是人一懶做了一次說明以後就沒影了,最近把程式碼整理了一下,準備發到github上,在這裡,再把這個框架總結一下吧。 多執行緒一直是程式設計中常見的問題,特別是在Linux的c++上,多執行緒的封裝一直不是很好,當然,

C++ 多執行框架3訊息佇列

之前,多執行緒一些基本的東西,包括執行緒建立,互斥鎖,訊號量,我們都已經封裝,下面來看看訊息佇列 我們儘量少用系統自帶的訊息佇列(比如Linux的sys/msgqueue),那樣移植性不是很強,我們希望的訊息佇列,在訊息打包和提取都是用的標準的C++資料結構,當然,

執行學習停止執行

停止執行緒 停止一個執行緒可以使用Thread.stop()方法,但最好不用它,因為這個方法是不安全的,而且已被棄用。 大多數停止一個執行緒的操作使用Thread.interrupt()方法,但是這個方法不會終止一個正在執行的執行緒,還需要加入一個判斷才可以完成執行緒的停止。 Jav

執行學習isAlive()和sleep()和getId()

isAlive() isAlive()判斷執行緒是否處於活動狀態,即執行緒已經啟動但尚未終止。 例一 public class MyThread extends Thread{ @Override public void run() { System.out.prin

執行學習執行間的資料共享

資料不共享的情況 public class MyThread04 extends Thread{ private int count=5; public MyThread04(String threadName) { this.setName(threadName); }

執行學習執行的兩種實現方式

程序和執行緒 程序是受作業系統管理的基本執行單元。 執行緒是在程序中獨立執行的子任務。 多執行緒的優點 使用多執行緒技術後,可以在同一時間內執行更多不同種類的任務。 單執行緒是同步執行任務,多執行緒是非同步執行任務。 多執行緒的使用方式 1:繼承Thread 2:實現

執行同步執行安全處理Synchronized

一  執行緒同步 java中提供了執行緒同步機制,它能夠解決上述的執行緒安全問題。          執行緒同步的方式有兩種: 方式1:同步程式碼塊   方式2:同步方法 1 同步程式碼塊 同步程式

Linux--執行安全-

文章目錄 條件變數 Posix訊號量 2、訊號量的操作(等待/通知) 3、訊號量的釋放 posix執行緒互斥實現 條件變數 需要一個條件:表示臨界區有沒有資源 為什麼

Java多執行程式設計-15-讀寫ReentrantReadWriteLock深入分析

上兩篇: 一、前言 上兩篇的內容中已經介紹到了鎖的實現主要有ReentrantLock和ReentrantReadWriteLock。 ReentrantLock是重入鎖,顧名思義就是支援重進入的鎖,他表示該鎖能夠支援一個執行緒對資源的重複加鎖

Java執行總結併發包------讀寫ReadWriteLock的簡單例子詳細理解

初次接觸ReadWriteLock類時也在網上查了很多資料,很容易瞭解到ReadWriteLock是讀寫鎖,並且讀寫鎖的機制有以下三個特點:  讀鎖---讀鎖    (不互斥)  讀鎖---寫鎖     (互斥)  寫鎖---寫鎖     (互斥)什麼意思呢?網上很多資料,

JAVA多執行入門JAVA中如何寫多執行

第一種方式:繼承Thread 步驟: 1.建立執行緒類,繼承自Thread + 重寫run,run中寫執行緒體,執行緒體就是mian()函式裡面的寫法 2.使用執行緒: 2.1 建立執行緒物件 2.2 執行緒物件.start() 步驟展示: 1. public

c++11多執行程式設計joining和detaching 執行

Joining執行緒 執行緒一旦啟動,另一個執行緒可以通過呼叫std::thread物件上呼叫join()函式等待這個執行緒執行完畢std::thread th(funcPtr); th.join(); 看一個例子主執行緒啟動10個工作執行緒,啟動完畢後,main函式等待

C#淺談執行獨立執行池的作用及IO執行

在上一篇文章中,我們簡單討論了執行緒池的作用,以及CLR執行緒池的一些特性。不過關於執行緒池的基本概念還沒有結束,這次我們再來補充一些必要的資訊,有助於我們在程式中選擇合適的使用方式。 獨立執行緒池 上次我們討論到,在一個.NET應用程式中會有一個CLR執行緒池,可以使用Th

執行開發1執行,前臺執行,後臺執行,守護執行,子執行,託管執行的關係

1.主執行緒 當一個程式啟動時,就有一個程序被作業系統(OS)建立,與此同時一個執行緒也立刻執行,該執行緒通常叫做程式的主執行緒(Main Thread),因為它是程式開始時就執行的,如果你需要再建立執行緒,那麼建立的執行緒就是這個主執行緒的子執行緒。每個程序至少都有一個