在Parallel.ForEach或For循环中的返回值和关键字

7

如何正确地从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;
}

最后,在Parallel For或ForEach循环中,return关键字的行为是什么?是否会立即终止所有线程?它会导致任何意外的副作用吗?希望我的问题表述清楚了。请注意,第一个例子中修改的String是线程安全的,并且包含我期望的值吗?在第二个例子中,我的List Collection修改是线程安全的吗?所有我期望添加的值都会被添加吗?请留意HTML标签。
4个回答

9

我会在这里使用 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()

肯定更合适吧?


我只是在尝试玩弄这个概念。我猜它可能不会有什么影响。 - Maxim Gershkovich
只有我一个人吗?上面的代码抛出了以下错误:Win32Exception未被用户代码处理。拒绝访问——我猜这与比较句柄有关? - Maxim Gershkovich
不是我的情况,我有自己定制的窗口类。它实现了进程对象没有提供的功能。我只是试图避免另一个API调用,并尝试玩弄这个概念。看起来在标准循环中也无法工作。糟糕! - Maxim Gershkovich

6

return 在并行 foreach 循环中的作用基本上相当于在普通循环中使用 continue,因为 return 是在为每个项目执行的委托内部。这意味着它不会终止任何线程,特别是不会终止并行 foreach 循环的执行。


6

除了以上的答案 +:

如果你想要停止循环,你应该使用 ParallelLoopState (你可以通过传递 Action<T, ParallelLoopState> 而不是 Action<T> 委托来访问此对象。状态对象具有像 .Stop().Break() 这样的方法,这将表示您希望停止执行循环(还有一些其他有用的属性,请自行尝试)。


5

两种方法都不正确。您是在搜索一个值吗? Parallel.ForEach 不是正确的解决方案。您需要的是一种归约,而 ForEach 执行的是映射。可以使用 PLINQ 进行并行归约。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接