2016-10-24 3 views
-1

У меня есть количество уведомлений о событиях, происходящих каждый раз, например, файл добавляется в папку (это не мой случай, а просто аналогия для объяснения).Задачи процесса в заказе

В папку может быть добавлено любое количество файлов, мы не знаем, сколько их будет.

В настоящее время мое событие myEvent_Handler срабатывает каждый раз, когда файл добавляется

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

Проблема в выше обработчика событий является состояние гонки кстати линии, отмеченной (1) и (2). Например, событие вызывается 4 раза:

  1. (1) истинно, (2) находится в очереди в задаче для запуска
  2. (1) истинно, (2) находится в очереди в задачу запустить
  3. (1) истинно, (2) ставится в очередь задачи для запуска
  4. (1) истинно, (2) ставится в очередь задачи для запуска

Теперь очередь задач имеет 4 задачи для выполнения, но они не выполняются в том же порядке. Они могут выполняться в любом порядке, т. Е. 3-> 2-> 4-> 1, но мне нужно, чтобы они выполнялись как 1-> 2-> 3-> 4 без блокировки нити пользовательского интерфейса.

Как я могу достичь этого, не блокируя поток пользовательского интерфейса?

+2

Похоже, что очередь задач – stuartd

+3

звучит так, как будто вы можете использовать задачу с большим количеством задач с очередью. см. это для решения: http://stackoverflow.com/questions/2293976/how-and-if-to-write-a-single-consumer-queue-using-the-tpl – muratgu

+2

[Очередь] (https: // msdn.microsoft.com/en-us/library/system.collections.queue(v=vs.110).aspx) или, возможно, [потоковая безопасная очередь] (http://stackoverflow.com/questions/13416889/thread-safe -queue-Епдиеие-Dequeue)? –

ответ

-2

я понял это и переписал мой код выше, чтобы сделать это:

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

Это работает большой и не блокирует мой UI поток.

ОБНОВЛЕНИЕ: Хотя я обнаружил, что это отлично работает для меня (это фиксировало мое состояние гонки, и мой поток пользовательского интерфейса не был заблокирован), после дальнейшего расследования я обнаружил, что метод, вызывающий этот код, не выполнялся в потоке пользовательского интерфейса (что объясняет, почему этот код не блокировал мой поток пользовательского интерфейса), но, проверяя ManagedThreadId, я обнаружил, что указанная выше задача работает в том же потоке, на котором работает этот метод. В результате, я изменил свое осуществление в соответствии с этими 2 сообщений:.

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

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

использовать LimitedConcurrencyLevelTaskScheduler (при условии, в приведенной выше статье MSDN

Это решение хорошо работает, задача не выполняется в том же потоке, что и метод, в котором задана задача, которая увеличивает производительность.

+0

[Вы почти никогда не должны использовать конструктор задачи. Этот пример не является исключением.] (Http://blog.stephencleary.com/2014/05/a-tour-of-task-part-1-constructors.html). –

+0

Не могли бы вы разработать @ErikPhilips – pixel

+0

Я не думаю, что переписывание того, что говорит блог, связано с какой-либо ценностью. –

0

Я бы посмотрел на используя для этого реактивную платформу Microsoft (NuGet «System.Reactive»). Это идеально.

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

Он будет автоматически маршалом в фоновом потоке (Scheduler.Default) и гарантирует, что каждая подписка на вызов, ожидающий, пока предыдущая не будет завершена, прежде чем двигаться дальше.

Это могло бы полностью заменить метод myEvent_Handler.

.Subscribe(...) call возвращает IDispose, и вы можете использовать его для прекращения обработки в любое время - он эффективно отделяется от события.

Смежные вопросы