c# - 总结为双倍时从 ForEach 循环转换为 Parallel.ForEach 循环会减慢速度

标签 c# thread-safety parallel.foreach

我有一段 C# 代码如下。此代码总结了 DataTable 中的一列“ double ”:

var data = this.Db.ExecuteRead(query, this.Score.Name);
var time = 0.0;
foreach (DataRow row in data.Rows)
{
    time += this.ParseDouble(row[0].ToString()) / MillisecondsPerMinute;
}
执行此代码需要 4 秒。我想加快速度,所以我将它并行化如下:
Parallel.ForEach(
                data.AsEnumerable(),
                row =>
                    {
                        time += this.ParseDouble(row[0].ToString()) / MillisecondsPerMinute;
                    });
此代码需要 3 秒才能执行。它也会导致碰撞。我不认为“双”线程安全。这是预料之中的。然后我添加了一个互斥体以使其线程安全:
Parallel.ForEach(
                data.AsEnumerable(),
                row =>
                    {
                        mut.WaitOne();
                        ptime += this.ParseDouble(row[0].ToString()) / MillisecondsPerMinute;
                        mut.ReleaseMutex();
                    });
这段代码要慢得多。执行需要 15 秒,但会产生准确的结果。我的问题是,我最好在这里使用标准的“ForEach”,还是可以以更好的方式实现多线程?
作为引用,这里是 ParseDouble 方法:
protected double ParseDouble(string text)
{
    double value;
    if (!double.TryParse(text, out value))
    {
        throw new DoubleExpectedException();
    }

    return value;
}

最佳答案

这里有一些方法。先来个简单的Parallel.ForEach ,将 protected 区域 ( lock ) 减少到所需的绝对最小值(更新共享状态)。这应该最大限度地减少对锁的争用。

DataTable data = this.Db.ExecuteRead(query, this.Score.Name);
double totalTime = 0.0;
Parallel.ForEach(data.AsEnumerable(), row =>
{
    double time = Double.Parse(row[0].ToString()) / MillisecondsPerMinute;
    lock (data) { totalTime += time; }
});
PLINQ方法。简单安全,但可能不是最有效的:
double totalTime = data
    .AsEnumerable()
    .AsParallel()
    .Select(row => Double.Parse(row[0].ToString()) / MillisecondsPerMinute)
    .Sum();
Parallel.ForEach的组合和 Partitioner.Create 应该提供最佳性能,因为它允许对工作负载进行分块:
double totalTime = 0.0;
Parallel.ForEach(Partitioner.Create(0, data.Rows.Count), () => 0.0D,
    (range, state, accumulator) =>
{
    for (int i = range.Item1; i < range.Item2; i++)
    {
        DataRow row = data.Rows[i];
        accumulator += Double.Parse(row[0].ToString()) / MillisecondsPerMinute;
    }
    return accumulator;
}, accumulator =>
{
    lock (data) { totalTime += accumulator; }
});

关于c# - 总结为双倍时从 ForEach 循环转换为 Parallel.ForEach 循环会减慢速度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65204040/

相关文章:

c# - 我收到错误消息,其中包含 JSON 对象中的字节数组字段

c# - 如何检查 PCL 项目 xamarin 中的服务器是否可达?

c# - 我的 Blazor 服务器应用程序在一台本地计算机上的 Azure B2c 登录中遇到空引用错误,但在另一台本地计算机上却没有,有什么想法吗?

c# - 在不重新编译项目的情况下升级 C# 项目中的引用 dll

java - 如何保护单例类方法在 Java 中是线程安全的?

java - 解释导致 HashMap.put() 执行无限循环的时机

process - Keras "pickle_safe": What does it mean to be "pickle safe", 或者 Python 中的 "non picklable"?

R 并行处理错误 `Error in checkForRemoteErrors(val) : 6 nodes produced errors; first error: subscript out of bounds`

c# - 如何指定Parallel.ForEach中执行的并行任务数?

c# - 从 Parallel.ForEach 抛出时未处理的 OperationCanceledException