我正在深入学习4.0框架中的Parallel类,并试图了解它何时有用。在查看了一些文档之后,我尝试执行两个循环,一个使用Parallel.Invoke,一个顺序如下:
static void Main()
{
DateTime start = DateTime.Now;
Parallel.Invoke(BasicAction, BasicAction2);
DateTime end = DateTime.Now;
var parallel = end.Subtract(start).TotalSeconds;
start = DateTime.Now;
BasicAction();
BasicAction2();
end = DateTime.Now;
var sequential = end.Subtract(start).TotalSeconds;
Console.WriteLine("Parallel:{0}", parallel.ToString());
Console.WriteLine("Sequential:{0}", sequential.ToString());
Console.Read();
}
static void BasicAction()
{
for (int i = 0; i < 10000; i++)
{
Console.WriteLine("Method=BasicAction, Thread={0}, i={1}", Thread.CurrentThread.ManagedThreadId, i.ToString());
}
}
static void BasicAction2()
{
for (int i = 0; i < 10000; i++)
{
Console.WriteLine("Method=BasicAction2, Thread={0}, i={1}", Thread.CurrentThread.ManagedThreadId, i.ToString());
}
}
此处的执行时间没有明显差异,或者我错过了这一点?它是否对Web服务的异步调用或...更有用?
编辑:我使用秒表删除了DateTime,通过简单的添加操作删除了对控制台的写入。
更新 - 现在是时差:感谢您清理我在参与控制台时遇到的问题
static void Main()
{
Stopwatch s = new Stopwatch();
s.Start();
Parallel.Invoke(BasicAction, BasicAction2);
s.Stop();
var parallel = s.ElapsedMilliseconds;
s.Reset();
s.Start();
BasicAction();
BasicAction2();
s.Stop();
var sequential = s.ElapsedMilliseconds;
Console.WriteLine("Parallel:{0}", parallel.ToString());
Console.WriteLine("Sequential:{0}", sequential.ToString());
Console.Read();
}
static void BasicAction()
{
Thread.Sleep(100);
}
static void BasicAction2()
{
Thread.Sleep(100);
}
答案 0 :(得分:6)
你正在做的测试是荒谬的;如果你并行执行它,你正在测试看你不能并行执行的东西会更快。
Console.Writeline为您处理同步,因此它将始终表现为在单个线程上运行。
来自here:
...分别调用SetIn,SetOut或SetError方法。 I / O 使用这些流的操作是同步的,这意味着多个 线程可以读取或写入流。
并行版本在多个线程上运行所获得的任何优势都会因控制台的编组而丢失。事实上,我不会惊讶地发现所有线程切换实际上意味着并行运行更慢。
尝试在操作中执行其他操作(一个简单的Thread.Sleep会这样做)可以同时处理多个线程,并且您应该看到运行时间的巨大差异。足够大以至于使用DateTime作为计时机制的不准确性并不重要。
答案 1 :(得分:2)
这不是执行时间的问题。控制台的输出取决于操作的运行方式。要获得准确的执行时间,您应该使用StopWatch。无论如何,您正在使用Console.Writeline
,因此它看起来好像在一个执行线程中。您使用parallel.invoke
尝试获得的任何内容都因Console.Writeline
的性质而丢失。
答案 2 :(得分:1)
在简单的事情上,运行时间将是相同的。 Parallel.Invoke正在做的是同时运行这两个方法。
在第一种情况下,您将以混合顺序将线条吐出到控制台。
Method=BasicAction2, Thread=6, i=9776
Method=BasicAction, Thread=10, i=9985
// <snip>
Method=BasicAction, Thread=10, i=9999
Method=BasicAction2, Thread=6, i=9777
在第二种情况下,你将拥有BasicAction2之前的所有BasicAction。
这表明两种方法同时运行。
答案 3 :(得分:0)
在理想情况下(如果委托数等于并行线程数并且有足够的cpu核心),操作持续时间将变为MAX(AllDurations)
而不是SUM(AllDurations)
(如果AllDurations
是每个代理执行时间的列表,如{1秒,10秒,20秒,5秒})。在不太理想的情况下,它朝着这个方向前进。
当你不关心调用委托的顺序时它很有用,但是你关心你阻止线程执行直到每个委托完成,所以是的,你可能需要从各种来源收集数据在你可以继续之前(它们可以是web服务或其他类型的来源)。
我认为 Parallel.For
可以更频繁地使用,在这种情况下,它几乎要求你有不同的任务,并且每个任务都需要相当长的时间来执行,我想如果你不知道可能的执行时间范围(对于webservices来说都是如此)Invoke将发挥最大作用。
也许你的静态构造函数需要为你的类型构建两个独立的字典,你可以调用使用Invoke()并行填充它们的方法,如果它们大致花费相同的时间,则缩短时间2x。