Anonymous methods, ThreadPool, and captured variables

  • Thread starter Thread starter Anthony Paul
  • Start date Start date
A

Anthony Paul

Hello everyone,

I just ran into a problem today that I found rather weird but can't
think of a reason why it's occurring, so I'm putting it out here to
see if any gurus have a better understanding of what's going on.

I have a method that I use to facilitate asynchronous operations such
that it accepts two delegate parameters, one that gets executed by a
background thread via the ThreadPool and the other a delegate to call
on the UI thread once the background thread is done. Works great, no
problems, but then I ran into a situation where I wanted to execute
multiple work delegates instead of just one, and once they are all
done only then would the UI delegate get called. So I did that, and
here's my modified method :

protected void DoAsyncStuff(ThreadStart[] workDelegates, ThreadStart
uiDelegate)
{
int workers = workDelegates.Length;

foreach (ThreadStart workDelegate in workDelegates)
{
ThreadPool.QueueUserWorkItem
(
(WaitCallback)delegate
{
workDelegate();

if (uiDelegate != null)
{
if (Interlocked.Decrement(workers) == 0)
{
Dispatcher.BeginInvoke
(
(ThreadStart) delegate
{
uiDelegate();
},
System.Windows.Threading.DispatcherPriority.Normal
);
}
}
},
null
);
}
}

Now here's my method that uses it :

private void MyMethod()
{
int result1;
int result2;

DoAsyncStuff
(
new Delegate[]
{
delegate
{
result1 = Model.GetRandomNumber();
},
delegate
{
result2 = Model.GetRandomNumber();
}
},
delegate
{
// do something here with the results
}
);
}

Everything executes fine, but when both work delegates have finished
executing and the UI delegate executes, result2 is the only one that
has a value. result1 didn't get updated. If I reverse the order,
again, the first delegate doesn't seem to return a value, but the
second one does. Does anyone understand what's going on here?

Regards,

Anthony
 
Hmmm,

Shouldn't that be

if (Interlocked.Decrement(ref workers) == 0) ?

You're not waiting for your delegates to complete before trying to use the
results. If you put break points at

1) result1 = Model.GetRandomNumber();
2) result2 = Model.GetRandomNumber();
3) End of MyMethod()

I bet your debugger hits point 3 before 1 and 2 have finished.

As an easy way of testing this idea put System.Threading.Thread.Sleep(30000)
at the end of MyMethod. result1 and result 2 should be set by then.

HTH,

Adam
=========
 
Hi Adam,



Yes, you are correct, there should be a ref there... I was relying on
memory alone when I posted the code.



As for not waiting for the method to finish, that's not the case...
each thread is executing a workdelegate synchronously, so by the time
it gets to the Interlocked statement its already finished executing.
The interlocked is there simply to ensure that the UI delegate gets
called once every worker thread has finished executing.



Regards,



Anthony





Hmmm,

Shouldn't that be

if (Interlocked.Decrement(ref workers) == 0) ?

You're not waiting for your delegates to complete before trying to use the
results. If you put break points at

1) result1 = Model.GetRandomNumber();
2) result2 = Model.GetRandomNumber();
 
Back
Top