Race condition for thread startup
A similar question was asked here, but the answers generally all seem to relate to the lambda notation. I get a similar result without the lambda so I thought I'd ask for some clarification:
Say I have something like this:
for (int i = 0; i < 5; i++)
(new Thread(new ThreadStart(delegate()
{
Console.WriteLine("Thread " + i);
}))).Start();
One would expect the following output:
Thread 0
Thread 1
Thread 2
Thread 3
Thread 4
Now I realise that the threads aren't started in any particular order, so let's just assume that the above lines can come out in any order.
But that is not what happens. What instead happens:
Thread 3
Thread 4
Thread 4
Thread 4
Thread 4
or something similar, which leads me to believe that rather than passing the value if i, it is passing the reference. (Which is weird, 开发者_C百科since an int is a value type).
Doing something like this:
for (int i = 0; i < 5; i++)
(new Thread(new ThreadStart(delegate()
{
int j = i;
Console.WriteLine("Thread " + j);
}))).Start();
does not help either, even though we have made a copy of i. I am assuming the reason is that it hasn't made a copy of i in time.
Doing something like this:
for (int i = 0; i < 5; i++)
{
(new Thread(new ThreadStart(delegate()
{
Console.WriteLine("Thread " + i);
}))).Start();
Thread.Sleep(50);
}
seems to fix the problem, however it is extremely undesirable as we're wasting 50ms on each iteration, not to mention the fact that if the computer is heavily loaded then maybe 50ms may not be enough.
Here is a sample with my current, specific problem:
Thread t = new Thread(new ThreadStart(delgate()
{
threadLogic(param1, param2, param3, param4);
}));
t.Start();
param1 = param2 = param3 = param4 = null;
with:
void threadLogic(object param1, object param2, object param3, object param4)
{
// Do some stuff here...
}
I want threadLogic() to run in its own thread, however the above code gives a null reference exception. I assume this is because the values are set to null before the thread has had a chance to start.
Again, putting a Thread.Sleep(100) works, but it is an awful solution from every aspect. What do you guys recommend for this particular type of race condition?
You need to introduce a temporary:
for (int i = 0; i < 5; i++)
{
int temp = i; // Add this
(new Thread(new ThreadStart(delegate()
{
Console.WriteLine("Thread " + temp);
}))).Start();
}
The problem is in how delegates close around the outer variable (i
in your code, temp
in mine). The scope is wrong (outside the for loop), so by the time the thread starts, i
has already been incremented most if not all of the way.
For your second example, you need to do the same thing. Just make temporaries:
var temp1 = param1;
var temp2 = param2;
var temp3 = param3;
var temp4 = param4;
Thread t = new Thread(new ThreadStart(delgate()
{
threadLogic(temp1, temp2, temp3, temp4);
}));
t.Start();
// This is now safe, since the closure above is over "temp*"
param1 = param2 = param3 = param4 = null;
Your issue is the same; it's not the lambda syntax itself, it's the fact that you're closing over a local variable in an anonymous method (the delegate
syntax you're using was the first iteration of anonymous methods, which made its debut in .NET 2.0).
If you want to do this, you'll have you use a workaround:
for (int i = 0; i < 5; i++)
{
int j = i;
(new Thread(new ThreadStart(delegate()
{
Console.WriteLine("Thread " + j);
}))).Start();
}
Note that this is similar to what you tried (copying), but it needs to be outside of the closure and inside the loop. Copying it within the anonymous function (like in your example) doesn't help.
精彩评论