-4

I have a method "Add2List", who creates a ManualResetEvent and stores it in SortedList of a instance, then waits for signaling, then do some work and dispose the event.
I have another method "DoSomething", who listens to remote server and then signals the stored manual events according to Guid.

in the multithreading context, multi threads calls method "Add2List", so in the sortedlist there may have several manual event with same name at the same moment. But this may cause chaos. How should i avoid this?

To be simpler, i wrote this test code:

Class Program
{
  static void Main(string[] args)
  {
    StringBuilder str = new StringBuilder();//a string to record what happened
    //test iteratively
    for(int i=0;i<100;i++)
    {
      EventHolder holder = new EventHolder();
      Signaler ob2 = new Signaler();
      Thread th1 = new Thread(holder.Add2List);
      Thread th2 = new Thread(holder.Add2List);
      Thread th3 = new Thread(ob2.DoSomething);
      th1.Start(1);
      th2.Start(2);
      th3.Start();
      //Make sure all thread is ended before the next iteration.
      while(th1.IsAlive){ Thread.Sleep(200); }
      while(th2.IsAlive){ Thread.Sleep(200); }
      while(th3.IsAlive){ Thread.Sleep(200); }
    }
    Console.Read();
  }

  public class EventHolder
  {
    static SortedList<int, ManualResetEvent> MyManualEventList = new SortedList<int, ManualResetEvent>();
    public EventHolder()
    {
      MyManualEventList = new SortedList<int, ManualResetEvent>();
      Signaler.SignalMyManualEvent += OnSignalMyManualEvent;
    }
    void OnSignalMyManualEvent(int listindex)
    {
      try { MyManualEventList[listindex].Set(); }
      catch(Exception e)
      {
        Console.WriteLine("Exception throws at " + System.DateTime.Now.ToString() +" Exception Message:"
        Console.WriteLine(e.Message);
        int temp = 0; //*Here is a breakpoint! To watch local variables when exception happens.
      }
    }
    public void Add2List(object listindex)
    {
      ManualResetEvent MyManualEvent = new ManualResetEvent(false);
      MyManualEvent.Reset();
      MyManualEventList.Add((int)listindex, eve);
      //in this test, this countdownevent need to be signaled twice, for it has to wait until all 2 event been added to MyManualEventList
      Signaler.StartTrySignal.Signal();
      MyManualEvent.WaitOne();
      Console.WriteLine("Event" + ((int)listindex).ToString() + " been detected at " + System.DateTime.Now.Tostring());
      MyManualEvent.Dispose();
    }
  }

  public class Signaler
  {
    public delegate void Signalhandler(int listindex);
    public static event Signalhandler SignalMyManualEvent;
    public static CountDownEvent StartTrySignal = new CountDownEvent(2); // signaled twice so that the 2 manual events were added to sortedlist
    void RaiseSignalMyManualEvent(int listindex)
    {
      var vr = SignalMyManualEvent;
      if(vr != null)
        vr(listindex);
    }
    int i = 0, j = 0, k = 0;
    // here i use 2 prime numbers to simulate the happening of 2 random events
    public Signaler()
    {
      StartTrySignal.Reset();
    }
    public void DoSomething()
    {
      StartTrySignal.Wait(); // wait for the 2 manual events been added to sortedlist
      //To signal MyManualEventList[1] or MyManualEventList[2]
      while(i + j == 0)
      {
        Random rnd = new Random();
        k = rnd.Next();
        if(k % 613 == 0) { i = 1; Console.WriteLine("Event1 Raised!"; RaiseSignalMyManualEvent(1); }
        else if(k % 617 == 0) { j = 1;  Console.WriteLine("Event1 Raised!"; RaiseSignalMyManualEvent(2); }
      }
      //if MyManualEventList[1] has not been signaled, wait something to happen, and signal it.
      while(i == 0)
      {
        Random rnd = new Random();
        k = rnd.Next();
        if(k % 613 == 0)
        {
          i = 1;
          if(j>0)
          {
            m++;
            Console.WriteLine("All 2 Events Raised!  - iteration " + m.ToString());
          }
          RaiseSignalMyManualEvent(1);
        }
      }
      //if MyManualEventList[2] has not been signaled, wait something to happen, and signal it.
      while(j == 0)
      {
        Random rnd = new Random();
        k = rnd.Next();
        if(k % 617 == 0)
        {
          j = 1;
          m++;
          Console.WriteLine("All 2 Events Raised!  - iteration " + m.ToString());
          RaiseSignalMyManualEvent(2); 
        }
      }
    }
  }
  public class Counter //Provide a number to record iteration
  {
    public static int m = 0;
  }
}

Result: Sorry for do not have enough reputation to post images.
At the line where there's a breakpoint, system throws exception " the given key is not in dictionary". this exception happens randomly, sometimes because th1 disposed <2, MyManualEvent> or th2 disposed <1, MyManualEvent> , sometimes none has been disposed but it just cannot find anyone. I run this program 3 times, exception happens at iteration12, iteration45, and iteration0 (at the beginning).

Flexo
  • 87,323
  • 22
  • 191
  • 272
  • Do not change your question into an entirely new one once people have generously given their time to help you and you've got your answer. Doing so prevents future readers from benefitting from the same answer. – Flexo Apr 10 '15 at 06:25

1 Answers1

1

OK 2 answers

1: Your code returns "Event 1" after "All events", because the two console.writelines are in a race condition (the last while loop is never iterated)

2: the 'System' distingushes between the two ManualResetEvent objects becuase it references the SortedList you put them in. ie.

static SortedList<int, ManualResetEvent> MyManualEventList 
       = new SortedList<int, ManualResetEvent>();

  public EventHolder() { Signaler.SignalMyManualEvent 
       += OnSignalMyManualEvent; }

  void OnSignalMyManualEvent(int listindex)
  {

MyManualEventList[listindex].Set();

  }

when you raise event 1 you call set on Item 1 in the SortedList and when you raise Event 2 you call set on Item 2 in the list.

this is bad because the calling code has no idea which thread it is allowing to continue and you could well get a null exception

Ewan
  • 1,261
  • 1
  • 14
  • 25