Can somebody explain this odd behavior when working with ThreadPool?

孤者浪人 提交于 2019-11-26 18:37:01

问题


The Code

using System;
using System.Threading;

public delegate void LoadingProgressCallback(double PercentComplete,string ItemName);
public delegate void LoadCompleteCallback(int ItemID, string ItemName);

public static class Program
{
    public static void Main(string[] args)
    {
        LoadTest loadTest = new LoadTest();
        loadTest.LoadItems(args);
    }
}

public class LoadTest
{       
    ManualResetEvent resetEvent;
    int numThreads = 0;

    public LoadTest()
    {}

    public void LoadItems(string[] Items)
    {
        numThreads = 0;
        resetEvent = new ManualResetEvent(false);

        foreach(string item in Items)
        {
            Console.WriteLine("Adding {0} to ThreadPool",item);
            ThreadPool.QueueUserWorkItem
            (
                delegate
                {
                    Load(item, this.progCall, this.compCall);
                }
            );
            numThreads++;

            Thread.Sleep(100);//Remove this line

        }
        resetEvent.WaitOne();
    }

    public void progCall(double PercentComplete, string ItemName)
    {
        Console.WriteLine("{0}: is {1}% Complete [THREAD:{2}]",ItemName,PercentComplete.ToString(),Thread.CurrentThread.ManagedThreadId.ToString());
    }
    public void compCall(int ItemID, string ItemName)
    {
        Console.WriteLine("{0}: is Complete",ItemName);
        numThreads--;
        if(numThreads == 0)
        {
            resetEvent.Set();
        }
    }

    public void Load(string Item, LoadingProgressCallback progressCallback, LoadCompleteCallback completeCallback)
    {
        Console.WriteLine("Loading: {0} [THREAD:{1}]",Item,Thread.CurrentThread.ManagedThreadId.ToString());

        for(int i = 0; i <= 100; i++)
        {
            if(progressCallback != null)
            {
                progressCallback((double)i, Item);
            }
            Thread.Sleep(100);
        }
        if(completeCallback != null)
        {
            completeCallback(0,Item);
        }
    }
}

Observation

If I run this program from the command line, like this...

>TheProgram item1 item2

The output will look like this.

Adding item1 to ThreadPool
Loading: item1 [THREAD:3]
item1: is 0% Complete [THREAD:3]
Adding item2 to ThreadPool
Loading: item2 [THREAD:4]
item2: is 0% Complete [THREAD:4]
item1: is 1% Complete [THREAD:3]
item2: is 1% Complete [THREAD:4]
item1: is 2% Complete [THREAD:3]
item2: is 2% Complete [THREAD:4]

However, if I remove this line.

Thread.Sleep(100);//Remove this line

From the LoadItems method, the output looks like this.

Adding item1 to ThreadPool
Adding item2 to ThreadPool
Loading: item2 [THREAD:4]
Loading: item2 [THREAD:3]
item2: is 0% Complete [THREAD:4]
item2: is 0% Complete [THREAD:3]
item2: is 1% Complete [THREAD:4]
item2: is 1% Complete [THREAD:3]
item2: is 2% Complete [THREAD:3]
item2: is 2% Complete [THREAD:4]

The Question

It seems as though two threads are being used, though they both seem to be acting on the same data. Why does the code behave this way?


回答1:


You're closing over the loop variable, which gives you an unexpected result. Try this instead:

foreach(string item in Items)
{
    string item2 = item;
    Console.WriteLine("Adding {0} to ThreadPool", item2);
    ThreadPool.QueueUserWorkItem
    (
        delegate
        {
            Load(item2, this.progCall, this.compCall);
        }
    );
    numThreads++;

    Thread.Sleep(100);//Remove this line

}

References

  • Closing over the Loop Variable in C#
  • Closing over the loop variable considered harmful



回答2:


One thing that immediately comes to mind looking at the code is lack of use of Interlocked.

You have to use it otherwise you will see strange errors and behaviours.

So instead of

numThreads++;

Use:

Interlocked.Increment(ref numThreads);


来源:https://stackoverflow.com/questions/9992866/can-somebody-explain-this-odd-behavior-when-working-with-threadpool

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!