-1

I have number of event notifications happening each time for example a file is added to a folder (this is not my case but just an analogy to explain).

There could be any number of files added to folder, we dont know how many will there be.

Currently, my event myEvent_Handler fires each time a file is added

private void myEvent_Handler(object sender, SomeEvent e)
{
  // I dont know how many times this event will fire
  if (something == true) (1)
  {
    var t = Task.Run(() =>
    {
      DoSomething(e);  (2)
    });
  }
}

The problem in above event handler is the race condition btw line marked with (1) and (2). For example, the event is fired 4 times:

  1. (1) is true, (2) is queued in a task to run
  2. (1) is true, (2) is queued in a task to run
  3. (1) is true, (2) is queued in a task to run
  4. (1) is true, (2) is queued in a task to run

Now, tasks queue has 4 tasks to run but these are not executed in same order. They could execute in any order, i.e. 3->2->4->1 but I need them to execute as 1->2->3->4 without blocking UI thread.

How can I achieve this without blocking UI thread?

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
pixel
  • 9,653
  • 16
  • 82
  • 149
  • 2
    Sounds like a queue of tasks – stuartd Oct 24 '16 at 23:46
  • 3
    sounds like you can use a long running task with a queue. see this for a solution: http://stackoverflow.com/questions/2293976/how-and-if-to-write-a-single-consumer-queue-using-the-tpl – muratgu Oct 24 '16 at 23:57
  • 2
    [Queue](https://msdn.microsoft.com/en-us/library/system.collections.queue(v=vs.110).aspx) or maybe [thread safe Queue](http://stackoverflow.com/questions/13416889/thread-safe-queue-enqueue-dequeue)? – Alexei Levenkov Oct 25 '16 at 00:20
  • Thanks but my biggest problem is that all what I have read so far require client to know when to flag the end of loading a queue and I am not able to wrap my head around how to make this to work for my scenario. – pixel Oct 25 '16 at 21:04
  • Use [ContinueWith](https://msdn.microsoft.com/en-us/library/system.threading.tasks.task.continuewith(v=vs.110).aspx) method to chain multiple tasks. – Alexander Petrov Oct 25 '16 at 21:59
  • how would I do that in my example above? Thanks – pixel Oct 25 '16 at 22:25

2 Answers2

0

I would look at using Microsoft's Reactive Framework (NuGet "System.Reactive") for this. It's ideal.

Observable
    .FromEventPattern<EventHandler, EventArgs>(
        h => myEvent.Handler += h, h => myEvent.Handler -= h)
    .ObserveOn(Scheduler.Default)
    .Subscribe(ep => DoSomething(ep.EventArgs));

It will automatically marshall to a background thread (Scheduler.Default) and it guarantees that each subscription call waits until the previous one is complete before moving on.

This could would entirely replace the myEvent_Handler method.

The .Subscribe(...) call returns IDispose and you can use that to stop processing at any time - it effectively detaches from the event.

Enigmativity
  • 113,464
  • 11
  • 89
  • 172
-2

I figured it out and rewrote my code above to do this:

private void myEvent_Handler(object sender, SomeEvent e)
{
  // I dont know how many times this event will fire
  Task t = new Task(() =>
  {
    if (something == true) 
    {
        DoSomething(e);  
    }
  });
  t.RunSynchronously();
}

That is working great and is not blocking my UI thread.

UPDATE: Although I have found that this was working great for me (it fixed my race condition and my UI thread was not blocked), upon further investigation, I found that the method calling this code was not executing on UI thread (which explains why this code was not blocking my UI thread) but checking for ManagedThreadId, I found that the task above was running on same thread on which this method is running. As a result, I changed my implementation according to these 2 posts:

https://msdn.microsoft.com/library/system.threading.tasks.taskscheduler.aspx

How (and if) to write a single-consumer queue using the TPL?

to use LimitedConcurrencyLevelTaskScheduler (provided in above MSDN article.

That solution works well, the task is not executing on same thread as the method in which task in instantiated which increases performance.

Community
  • 1
  • 1
pixel
  • 9,653
  • 16
  • 82
  • 149