views:

408

answers:

3

Hello, I am running into a bit of difficulty using the threadpool class and an array of ManualResetEvents. Below is a simple example of what I am doing. The problem is that in the DoWork method I am getting null references to the resetEvent[param as int] object.

Can't seem to figure what I'm doing wrong.

(edit: got the code block working)

private static volatile ManualResetEvent[] resetEvents = new ManualResetEvent[NumThreads];
public void UpdateServerData()
{
   for (int i = 0; i < NumThreads ; i++)
        {
            resetEvents[i] = new ManualResetEvent(false);
            ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork), (object) i);

        }
  WaitHandle.WaitAll(resetEvents);
}
private void DoWork(object param)
{
//do some random work
resetEvents[(int)param].Set();
}

EDIT: I have tried inserting a System.Threading.Thread.MemoryBarrier(); after each .Set() however i still get a null reference exception.

+1  A: 

You cannot use the as keyword to cast to int (since int is not a reference type). Use (int)param instead:

private void DoWork(object param)
{
    //do some random work
    resetEvents[(int)param].Set();
}

Another approach that I feel is cleaner is to pass the wait handle to the method instead:

public void UpdateServerData()
{
    for (int i = 0; i < NumThreads ; i++)
    {
        resetEvents[i] = new ManualResetEvent(false);
        ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork), resetEvents[i]);
    }
    WaitHandle.WaitAll(resetEvents);
}
private void DoWork(object param)
{
    //do some random work
    (param as ManualResetEvent).Set();
}

That way the worker method has no knowledge about how the wait handle is managed on the outside; and it also cannot reach wait handles for other threads by mistake.

Fredrik Mörk
I just quickly wrote a simple example. I didn't mean for it to be 100% syntatically correct but I will fix the example
Setheron
dont think you meant to use the param as the indexer....should be able to just do(param as ManualResetEvent).Set();
CSharpAtl
@CSharpAtl: yes, I noticed that. My code died the copy/paste death, I guess ;o)
Fredrik Mörk
+1  A: 

volatile ManualResetEvent[] doesn't mean that access to array elements follows the volatile semantics. Only access to the variable holding the reference to the array will be volatile. Try inserting a memory barrier after assigning the array element, or using Thread.VolatileWrite to set them, e.g.

Thread.VolatileWrite (ref resetEvents[i], new ManualResetEvent (false)) ;
Anton Tykhyy
Oh. I thought assigning volatile to the array causes the members to inhert the keyword.I'm not familiar with the other two ways you mentioned (memory barrier and Interlocked.Exchange)
Setheron
Actually Interlocked.Xxx isn't a good idea here, since you don't need to read-and-update your data.
Anton Tykhyy
A: 

Pretty much I found the issue was in

for (int i = 0; i < NumThreads ; i++)    
{        
resetEvents[i] = new ManualResetEvent(false);        
ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork), resetEvents[i]);    
}

Instead of declaring a new ManualResetEvent I simply called Reset(). The issue seemed to have been that that although I would use MemoryBarrier or locks, the physical memory wouldn't be updated yet so it would point to null.

Setheron