Возвращаемое значение и ключевое слово внутри цикла Parallel ForEach или For - PullRequest
7 голосов
/ 29 марта 2011

Как правильно вернуть значение из цикла Parallel ForEach или For?

Например, следующий код является правильным / поточно-безопасным?

{
    Process[] processes = Process.GetProcesses();
    String ProcessName = String.Empty;
    Parallel.ForEach(processes, curItem => {
        if (curItem.Handle == this._handle) {
            ProcessName = curItem.ProcessName;
            return;
        }
    });
    return ProcessName;
}

ИЛИ ЭТО?

{
    Process[] processes = Process.GetProcesses();
    List<String> ProcessNames = new List<String>();
    Parallel.ForEach(processes, curItem => {
            ProcessNames.Add(processes.ProcessName);
        }
    });
    return ProcessNames;
}

Наконец, каково поведение ключевого слова return внутри цикла Parallel For или ForEach?

IE: это немедленно завершает все потоки? Это вызовет какие-либо артефакты, которых вы не ожидаете?

Надеюсь, то, что я спрашиваю, имеет смысл.

PS: чтобы быть более конкретным. Глядя на первый пример, моя модификация потока String безопасна и содержит ожидаемое значение из-за оператора return? И во втором примере моя модификация Thread Collection безопасна? Будут ли добавлены все ожидаемые значения?

Ответы [ 4 ]

9 голосов
/ 29 марта 2011

Я бы использовал PLINQ здесь:

return Process
    .GetProcesses()
    .AsParallel()
    .SingleOrDefault(p => p.Handle == this._handle);

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

Чтобы ответить на ваш вопрос более прямо,для того, чтобы обработать ранний выход из параллельного цикла, вы должны рассмотреть перегрузки, которые передают ParallelLoopState исполняющему делегату.Это может использоваться для контроля преждевременного завершения цикла.

РЕДАКТИРОВАТЬ:

Вы видите ошибки, потому что ваш процесс не имеет достаточных привилегий для доступа к дескриптору проверяемых процессов.Грубым способом обработки этого будет следующий:

Process
.GetProcesses()
//.AsParallel()
.Where(p=>{try{var h=p.Handle;}catch{return false;}return true;})
.SingleOrDefault(p => p.Handle == this._handle)

Конечно, если предположить, что this._handle относится к дескриптору текущего выполняющегося процесса:

Process.GetCurrentProcess()

наверняка будетлучше подойдет?

6 голосов
/ 29 марта 2011

Все вышеперечисленные ответы +:

Если вы хотите сообщить, что цикл должен остановиться, вы должны использовать ParallelLoopState (вы можете получить доступ к этому объекту, передав Action<T, ParallelLoopState> вместо Action<T> делегат. StateУ объекта есть метод типа .Stop() или .Break(), который будет сигнализировать о том, что вы хотите прекратить выполнение цикла (и несколько других полезных свойств, попробуйте его самостоятельно).

6 голосов
/ 29 марта 2011

return внутри параллельного цикла foreach в основном совпадает с continue в обычном цикле, поскольку return находится внутри делегата, который выполняется для каждого элемента. Это означает, что он не завершает никакие потоки и, в особенности, не прерывает выполнение параллельного цикла foreach.

5 голосов
/ 29 марта 2011

Ни один из них не является правильным. Вы ищете значение? Parallel.ForEach это неправильное решение здесь. То, что вы ищете - это сокращение , в то время как ForEach выполняет отображение . Параллельное уменьшение можно выполнить с помощью PLINQ.

...