Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Producer-Consumer with a variation - How to synchronize with thread signal/wait?

While working on a large project I realized I was making a lot of calls to be scheduled in the future. Since these were fairly light-weight, I thought it might be better to use a separate scheduler.

ThreadPool.QueueUserWorkItem (() => 
{
    Thread.Sleep (5000);
    Foo (); // Call is to be executed after sometime
});

So I created a separate scheduler class that runs on its own thread and executes these events. I have 2 functions that access a shared queue from separate threads. I'd use a lock, but since one of the threads needs to sleep-wait, I wasn't sure how to release the lock.

class Scheduler
{
    SortedDictionary <DateTime, Action> _queue;
    EventWaitHandle _sync;

    // Runs on its own thread
    void Run ()
    {
        while (true)
        {
            // Calculate time till first event
            // If queue empty, use pre-defined value
            TimeSpan timeDiff = _queue.First().Key - DateTime.Now;

            // Execute action if in the next 100ms
            if (timeDiff < 100ms)
                ...
            // Wait on event handle for time
            else
                _sync.WaitOne (timeDiff);
        }
    }

    // Can be called by any thread
    void ScheduleEvent (Action action, DataTime time)
    {
        _queue.Add (time, action);
        // Signal thread to wake up and check again
        _sync.Set ();
    }
}

  • The trouble is, I'm not sure how to synchronize access to the queue between the 2 functions. I can't use a monitor or mutex, because Run() will sleep-wait, thus causing a deadlock. What is the right synchronization mechanism to use here? (If there a mechanism to atomically start the sleep-wait process and immediately release the lock, that might solve my problem)
  • How can I verify there is no race-condition?
  • Is this a variation of the producer consumer problem, or is there a more relevant synchronization problem-description?

    While this is somewhat geared towards C#, I'd be happy to hear a general solution to this. Thanks!
  • like image 786
    AK. Avatar asked Jan 22 '23 00:01

    AK.


    1 Answers

    The problem is easily solved, make sure the WaitOne is outside the lock.

      //untested
      while (true)
      {
          Action doit = null;
    
          // Calculate time till first event
          // If queue empty, use pre-defined value
          lock(_queueLock)
          {
             TimeSpan timeDiff = _queue.First().Key - DateTime.Now;
             if (timeDiff < 100ms)
                doit = _queue.Dequeue();
          }
          if (doit != null)
            // execute it
          else
             _sync.WaitOne (timeDiff);
      }
    

    _queueLock is a private helper object.

    like image 58
    Henk Holterman Avatar answered Apr 26 '23 23:04

    Henk Holterman