как справиться с этим состоянием гонки? - PullRequest
1 голос
/ 25 октября 2010

в моем классе я использую BackgroundWorker.в какой-то момент мне нужно отменить асинхронную операцию, которая может выполняться, и немедленно начать другую.код следует.одна вещь, в которой я не уверен, это состояние гонки, которое может возникнуть, если работник завершит работу прямо перед тем, как я назначу свою лямбду событию RunWorkerCompleted.если это произойдет, моя лямбда никогда не будет вызвана.комментарий в коде показывает это место.Любые комментарии о том, как справиться с этим?

спасибо Константин


if (this.worker.IsBusy)
{
    RunWorkerCompletedEventHandler f = null;

    f = (s, v) =>
    {
        this.RunWorkerCompleted -= f;
        this.worker.RunWorkerAsync();
    };

    // what if worker completes right before the following statement?
    this.worker.RunWorkerCompleted += f;
    this.worker.CancelAsync();
}
else
{
    this.worker.RunWorkerAsync();
}

Ответы [ 3 ]

4 голосов
/ 25 октября 2010

Пока этот код выполняется в главном потоке, гонки не будет.BGW может завершиться только после завершения работы обработчика события RunWorkerCompleted.Обработчик не может начать работать до тех пор, пока основной поток не войдет в цикл обработки сообщений.

Однако существует другой тип гонки, вызванный предложением else .Вы позволяете BGW запускать без обработчика событий RunWorkerCompleted.Теперь он может выполняться асинхронно, поскольку он не будет заблокирован. Всегда подписывайтесь на событие, тестируйте e.Отменено, чтобы знать, что произошло.

1 голос
/ 25 октября 2010

Вы можете просто добавить обработчик событий RunWorkerCompleted один раз в ctor, а также добавить переменную-член bool 'restart' в класс. Затем вы можете написать if (IsBusy) restart = true и в своем обработчике вы проверите, если (restart) Run (). Вы можете определить перезапуск как энергозависимый, чтобы избежать условий гонки в этом случае.

Я думаю, что в вашем случае не рекомендуется добавлять и удалять обработчики событий.

0 голосов
/ 25 октября 2010

Может быть, я просто недостаточно умен, чтобы понимать твой код.Но в моем мире я бы собрал Queue<Action> и заполнил бы все задания, которые необходимо выполнить.

Другой поток (или BackgroundWorker) взглянет на эту очередь и обработаетвсе задания в очереди последовательно (как мой ответ здесь ).Возможно, это не очень элегантно из-за режима вытягивания с использованием Thread.Sleep(1) в цикле.

Но этого можно достичь, создав BindingQueue<T>, который получен из Queue<T> и реализует IBindingList.Таким образом, вы можете просто подождать такого события, удалить из очереди и вызвать Action, пока очередь не опустеет и начать все заново.

...