簡體   English   中英

程序中的比賽條件

[英]Race condition in program

我是新手。 因此,我只是一個接一個地創建了4個專用線程(而不是使用Tasks),並為它們提供了一些有用的功能。 但是,這使我對startProcessing函數中的競爭條件產生了疑問,在這里我只是將currentIndex上的值存儲到鎖內的局部變量中。 但是,我跑了很多次,但不能滿足沒有種族問題或其他任何問題。

請幫我清除這個東西。

class Program
{

    private static object syncLock = new object();
    int num = 0;
    int currentIndex = 0;

    static void Main(string[] args)
    {
        Program p = new Program();
        p.num = 1000;
        p.callThreadCreation();

        while (p.num > 0)
        {
            Thread.Sleep(1000);
        }

        Console.WriteLine("All done");
    }

    public void callThreadCreation()
    {
        for (int i = 1; i <= 4; i++)
        {
            string name = "T" + i;
            Thread T = new Thread(new ThreadStart(() => startProcessing()));
            T.Name = name;
            T.Start();
        }

    }

    **private void startProcessing()
    {
        while (num > 0)
        {
            int tempIndex;
            lock (syncLock)
            {
                tempIndex = currentIndex;
            }
            Interlocked.Decrement(ref num);
            Interlocked.Increment(ref currentIndex);
            print(tempIndex);  
            Thread.Sleep(1000);
        }
    }**

    private void print(int x)
    {
        Console.WriteLine(x);
    }

}

考慮startProcessing方法。 想象一下,所有四個線程while到達while檢查。 由於循環檢查不同步,因此當num為2時,所有四個循環都可能進入循環。

  1. 首先線程到達while檢查。 看到num>0。進入循環。 釋放上下文。
  2. 第二個線程到達while檢查。 Num仍為2,因此> 0。
  3. 重復第三和第四線程。
  4. 現在您處於比賽狀態。 所有四個線程都在循環體內,並且不受while檢查的保護。 Decrement操作將完全是原子的並且是同步的,但是當這四個操作完成時,您仍將處於-2。 您有> 0的測試,但是它是在鎖定之外執行的。 因此,您的currentIndex可能會增加1000倍以上,我認為可能會不必要地進入循環的每個額外線程增加1003倍。

我已經稍微修改了您的原始程序(在Thread.SleepConsole.WriteLine()周圍移動,以使競爭條件更易於檢測。此外,我將最高計數更改為11,而不是4的倍數(該數字從理論上講,這根本不會改變行為,而只是改變發生的可能性

class Program
{
    private static object syncLock = new object();
    int num = 0;
    // make static so we can read in Main()
    static int currentIndex = 0;

    static void Main(string[] args)
    {
        Program p = new Program();
        p.num = 11;
        p.callThreadCreation();

        while (p.num > 0)
        {
            Thread.Sleep(1000);
        }

        Console.WriteLine("All done {0}", currentIndex);
        Console.Read();
    }

    public void callThreadCreation()
    {
        for (int i = 1; i <= 4; i++)
        {
            string name = "T" + i;
            Thread T = new Thread(new ThreadStart(() => startProcessing()));
            T.Name = name;
            T.Start();
        }
    }

    private void startProcessing()
    {
        while (num > 0)
        {
            int tempIndex;
            lock (syncLock)
            {
                tempIndex = currentIndex;
            }

            // putting this in front of the increment/decrement operations makes the race condition more likely
            print(tempIndex);
            Interlocked.Decrement(ref num);
            Interlocked.Increment(ref currentIndex);

            Thread.Sleep(1000);
        }
    }

    private void print(int x)
    {
        Console.WriteLine(x);
    }
}

如果嘗試此程序,最終應該在最后看到程序輸出12。 下面是我想出的解決比賽條件的程序。 我承認這不是最優雅的,但是在這樣一個簡單的示例中,它可能不需要那么優雅。

class Program
{
    private readonly object syncLock = new object();
    private int num = 0;
    private static int currentIndex = 0;

    private static void Main(string[] args)
    {
        Program p = new Program();
        p.num = 11;

        // just return the threads, so we can join
        var threadList = p.callThreadCreation();

        // don't need Thread.Sleep() here
        //while (p.num > 0)
        //{
        //    Thread.Sleep(1000);
        //}
        foreach (var t in threadList) { t.Join(); }

        Console.WriteLine("All done {0}", currentIndex);
        Console.Read();
    }

    public IReadOnlyList<Thread> callThreadCreation()
    {
        var threadList = new List<Thread>();

        for (int i = 1; i <= 4; i++)
        {
            // this is easier to read for me
            var T = new Thread(startProcessing)
            {
                Name = "T" + i
            };

            // just return the threads, so we can join
            threadList.Add(T);
            T.Start();
        }

        // just return the threads, so we can join
        return threadList;
    }

    private void startProcessing()
    {
        while (true)
        {
            int tempIndex;
            lock (syncLock)
            {
                // I'm not sure what the point of this line is... sometimes "tempIndex" will
                // be the same between two threads, but this just seems like a progress report
                // for the person watching the program so I didn't fix this.
                tempIndex = currentIndex;
                num--;
                if (num < 0) break;
            }

            print(tempIndex);
            Interlocked.Increment(ref currentIndex);
            Thread.Sleep(1000);
        }
    }

    private void print(int x)
    {
        Console.WriteLine(x);
    }
}

這應該沒有比賽條件。 這個想法是,對競爭值( num )的increment操作與檢查num值是否完成的鎖在同一鎖內。 在您的示例中,這是不正確的,因此可能會在不合時宜的時間進行上下文切換,以使num更改次數超過您想要的次數。 這也是為什么我更改了Console.WriteLine()發生位置的原因:增加了這種不適當的上下文切換的機會。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM