繁体   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