Является ли Interlocked.Decrement (i) правильным для Parallel.ForEach ()? - PullRequest
2 голосов
/ 02 сентября 2011

В прошлом я делал это:

List<Item> _Items = GetItems();
int _CountDown = _Items.Count;
using (BackgroundWorker _Worker = new BackgroundWorker())
{
    _Worker.DoWork += (s, arg) =>
    {
        DoSomething(_Items[_CountDown]);
    };
    _Worker.RunWorkerCompleted += (s, arg) =>
    {
        if (System.Threading.Interlocked.Decrement(ref _CountDown) == 0)
            RaiseAllDoneEvent();
        else
            _Worker.RunWorkerAsync();
    };
    _Worker.RunWorkerAsync();
}

С Parallel я делаю что-то вроде этого:

List<Item> _Items = GetItems();
int _CountDown = _Items.Count;
System.Threading.Tasks.Parallel.ForEach(_Items, (i) =>
{
    DoSomething(i);
    if (System.Threading.Interlocked.Decrement(ref _CountDown) == 0)
        RaiseAllDoneEvent();
});

Мой реальный вопрос: Правильно ли здесь Interlocked.Decrement ()?

1 Ответ

5 голосов
/ 02 сентября 2011

Parallel.ForEach будет блокироваться до тех пор, пока все элементы не будут обработаны - похоже, здесь нужно вызывать RaiseAllDoneEvent () из вызывающей стороны сразу после вызова Parallel.ForEach:

List<Item> _Items = GetItems();
System.Threading.Tasks.Parallel.ForEach(_Items, (i) =>
{
    DoSomething(i);
});
RaiseAllDoneEvent();

Другими словами, не нужно вообще отсчитывать (или отслеживать, сколько предметов нужно обработать).Если вы хотите, чтобы параллельные операции не блокировались, вы можете превратить их в кучу задач:

List<Item> _Items = GetItems();
Task.Factory.StartNew(()=> { 
        Task.WaitAll(
            _Items.Select(i => Task.Factory.StartNew(() => DoSomething(i))).ToArray()
        ); 
    }).ContinueWith(t => RaiseAllDoneEvent());

В этом случае вы начнете запускать внешнюю задачу, а затем подождать кучузадач (по одному для каждого элемента), а затем, наконец, поднять все готовое событие.Ничто из этого не блокирует первоначального абонента.

...