1

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.

4
  • I had done multiple edits to get the code block working. If anyone saw the earlier iterations, excuse the mess that it was. Commented Aug 20, 2009 at 15:12
  • I've also tried to lock every .Set() call because that should cause a volatile read/write of the object however that doesn't seem to work either. Very frustrating. Commented Aug 20, 2009 at 15:59
  • Why on earth do you issue a memory barrier after the Set()? You need to see the updated array element before you call Set() on it! Commented Aug 21, 2009 at 15:03
  • As a side note, I hope you're using RegisteredWaitHandle.Unregister after using the thread, as advised by msdn, unlike in the code here provided. Commented Sep 27, 2012 at 13:13

3 Answers 3

2

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.

Sign up to request clarification or add additional context in comments.

3 Comments

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

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)) ; 

2 Comments

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)
Actually Interlocked.Xxx isn't a good idea here, since you don't need to read-and-update your data.
0

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.