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?
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
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