C# 为什么我的代码不能使用多线程并行.For循环加速?
我尝试使用C# 为什么我的代码不能使用多线程并行.For循环加速?,c#,multithreading,parallel-processing,thread-safety,thread-local,C#,Multithreading,Parallel Processing,Thread Safety,Thread Local,我尝试使用System.Threading.Tasks库将简单的顺序循环转换为并行计算循环。 代码进行编译,返回正确的结果,但它不会节省任何计算成本,否则需要更长的时间 编辑:对不起,伙计们,我可能把这个问题简化了,并且犯了一些错误。 为了附加额外的信息,我正在i7-4700QM上运行代码,它在脚本中被引用。 这是实际的代码。我还切换到非线程局部变量 public static class LineNet { public static List<Ray>
System.Threading.Tasks
库将简单的顺序循环转换为并行计算循环。
代码进行编译,返回正确的结果,但它不会节省任何计算成本,否则需要更长的时间
编辑:对不起,伙计们,我可能把这个问题简化了,并且犯了一些错误。 为了附加额外的信息,我正在i7-4700QM上运行代码,它在脚本中被引用。 这是实际的代码。我还切换到非线程局部变量
public static class LineNet
{
public static List<Ray> SolveCpu(List<Speaker> sources, List<Receiver> targets, List<Panel> surfaces)
{
ConcurrentBag<Ray> rays = new ConcurrentBag<Ray>();
for (int i = 0; i < sources.Count; i++)
{
Parallel.For(
0,
targets.Count,
j =>
{
Line path = new Line(sources[i].Position, targets[j].Position);
Ray ray = new Ray(path, i, j);
if (Utils.CheckObstacles(ray,surfaces))
{
rays.Add(ray);
}
}
);
}
}
}
公共静态类线网
{
公共静态列表(列表源、列表目标、列表曲面)
{
ConcurrentBag射线=新ConcurrentBag();
for(int i=0;i
{
线路路径=新线(源[i]。位置,目标[j]。位置);
射线=新射线(路径i,j);
如果(使用检查障碍物(光线、表面))
{
射线。添加(射线);
}
}
);
}
}
}
蚱蜢实现只收集sources
targets
和surfaces
,调用方法solution
并返回ray
。
我知道向线程发送工作负载是很昂贵的,但是它是不是太贵了?
还是ConcurrentBag
只是阻止并行计算
另外,我的类是不可变的(?),但是如果我使用一个公共的列表
内核中止操作并抛出异常,有人能告诉我原因吗?如果没有可靠地重现问题的好方法,就不可能提供明确的答案。您发布的代码看起来甚至不是真实代码的摘录,因为声明为方法返回类型的类型与return
语句实际返回的值不同
但是,您发布的代码显然没有很好地使用Parallel.For()
。您的行
构造函数将花费相当高的成本来证明并行化创建项的任务的合理性。要清楚的是,这是这里唯一可能的胜利
最后,您仍然需要将您创建的所有行
实例聚合到一个列表中,因此为Parallel.for()
任务创建的所有中间列表都是纯开销。聚合必须序列化(即,一次只能有一个线程向结果
集合添加一个项),并且以最糟糕的方式(每个线程在放弃锁之前只能添加一个项,而另一个线程有机会获得它)
坦率地说,您最好将每个本地列表存储在一个集合中,然后在Parallel.For()
返回后在主线程中一次性聚合它们。这并不意味着代码的性能会比直接的非并行实现更好。但至少情况不会更糟。:)
底线是,您似乎没有一个可以从并行化中获益的工作负载。如果你不这么认为,你需要以更清晰、更详细的方式解释这种想法的基础
如果我使用一个公共列表,内核会中止操作并抛出异常,有人能告诉我为什么吗
您已经在使用(看起来)List
作为每个任务的本地数据,事实上这应该没问题,因为任务不共享本地数据
但是,如果您询问,如果您尝试使用List
而不是ConcurrentBag
作为result
变量,为什么会出现异常,那么这完全是意料之中的。List
类不是线程安全的,但是Parallel.For()
将允许它运行的每个任务与所有其他任务同时执行localFinally
委托。因此,您有多个线程都试图同时修改同一个非线程安全集合。这是灾难的秘诀。幸运的是你得到了例外;实际行为是未定义的,并且很可能会破坏数据结构,导致运行时异常。如果使方法运行速度减慢的是处理器,则并行循环通常只会使方法运行速度加快(也就是说,在四核计算机中,整个方法运行期间,CPU使用率保持在25%,因为处理器在整个时间内都在尽可能地工作。)如果这是真的,那么将其分散到多个线程上会有所帮助。如果您的方法在硬盘驱动器或网络资源或其他地方等待,则可能没有帮助。如果不了解有关如何生成行或程序正在执行的操作的更多详细信息,很难说为什么这样做很慢……有多少行我们在您的测试中讨论的是什么?会有一些初始开销,因此好处可能不会立即被注意到。另一件事是result.Add(line)
将阻止任何想要调用与您正在使用的ConcurrentBag相同的行的线程。
可能是信息性的。@ElementalPete I更新了问题以更好地解释问题。@TyCobb数十万行。源中有200点
和目标中有200点
,它配置了3.2s In并行模式,以及顺序计算中的2.9。@DourHighArch一个有助于调查问题的非常机智的答案我更新了这个问题。为了更好地解释我为什么要研究并行计算:我有两个列表,都有数百个点;我想将List1
中的每个点与List2
中的每个点连接起来。因为这会产生