【发布时间】:2020-12-19 16:12:52
【问题描述】:
我正在尝试使用多线程技术来减少计算时间。我的问题是计算独立项目的分数。例如,我有 10 个项目,需要获取每个项目的分数。我可以单线程连续使用十次来获取分数,也可以多线程并行使用。
在我的代码中,当我使用多线程代码时,计算时间平均减少了大约 7%。我预计多线程代码会将其减少约 50%。
我还将电源计划(控制面板>硬件和声音>电源选项)从平衡更改为高性能。
我用四个选项解决了同样的问题。
- 单线程和平衡选项:11 分钟
- 多线程和平衡选项:10 分 40 秒(减少 20 秒)
- 单线程和高性能:10 分钟
- 多线程和高性能:10 分 10 秒。(增加 10 秒)
我的问题是
- 下面的代码有什么问题?
- 为什么power选项设置高性能时,多线程代码的计算时间比单线程代码的计算时间增加(如上3和4情况)
- 我需要多少个内核来缩短计算时间?越多越好??
我是 C# 多线程世界的初学者。请指导我向右走。 提前感谢您的友好回答。
我做了一个简单的例子来解释我们的代码结构。该代码创建 10000 个项目并获取它们的分数。我们也使用“锁”来并行计算List结构。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SampleCode
{
class Program
{
static List<Item> Storage = new List<Item>();
static object key = new object();
static void Main(string[] args)
{
List<Item> items = CreateItems(10000);
int maxDegreeOfParallelism = 4;
Parallel.ForEach(items, new ParallelOptions { MaxDegreeOfParallelism = maxDegreeOfParallelism },
(item) => Evaluate(item));
}
public static void Evaluate(Item item)
{
item.Score = 0;
item.Score += GetScore1(item);
item.Score += GetScore2(item);
}
public static double GetScore1(Item item)
{
return item.UnitQty * 100;
}
public static double GetScore2(Item item)
{
lock (key)
{
if (item.UnitQty % 2 == 0)
Storage.Add(item);
else if (Storage.Count > 0)
Storage.RemoveAt(Storage.Count - 1);
return item.UnitQty * Storage.Count;
}
}
public static List<Item> CreateItems(int count)
{
List<Item> items = new List<Item>();
for(int i=0; i<count; i++)
{
items.Add(CreateItem(i));
}
return items;
}
public static Item CreateItem(int index)
{
Item item = new Item();
item.UnitQty = index % 25 + 1;
return item;
}
internal class Item
{
public double Score;
public int UnitQty;
}
}
}
【问题讨论】:
-
计算量大的部分可能在
lock语句中,所有线程都必须相互等待,因此您不会从额外的线程中看到太多好处。 -
欢迎您!想象一下让一个人将用户添加到一个列表中。这需要一定的时间。现在想象一下让 100 个人再次将相同的用户添加到同一个列表中。您必须确保同步该操作,因为一次只有一个人可以使用该列表。这就是您的 lock 语句发生的情况,没有速度提高。仅当计算/创建要添加的用户的实际任务需要很长时间时,并行性才有用。
-
我很难看到
GetScore2中的代码即使使用lock也可以是确定性的,因为它会根据数据从Storage中删除项目。这意味着处理项目的顺序很重要,并且多个线程争夺锁,这是不可预测的。这段代码的期望结果是什么? -
感谢您的关注。这是解释我的代码的简化代码。原始代码只是根据项目的优先级、位置、延迟程度等获得项目的确定性分数。
-
如果您可以删除对 Storage.RemoveAt 的调用,使用 PLinq 和 select 重写它应该很简单,这将消除围绕该锁的争用。但就目前而言,提供建议有点困难,因为预期的结果尚不清楚。
标签: c# multithreading parallel-processing locking performance-testing