您的 sn-ps 不等价。这是ParallelThread 的一个版本,它的作用与ParallelProcess 相同,但会启动新线程:
public static bool ParallelThread()
{
Stopwatch sw = new Stopwatch();
sw.Start();
var threads = new Thread[10];
for (int i = 0; i < 10; i++)
{
int x = i;
threads[i] = new Thread(() => Thread1(x));
threads[i].Start();
}
for (int i = 0; i < 10; i++)
{
threads[i].Join();
}
sw.Stop();
Console.WriteLine(string.Format("Time in secs {0}", sw.Elapsed.Seconds));
return true;
}
private static void Thread1(int x)
{
Console.WriteLine(string.Format("Printing {0} thread = {1}", x,
Thread.CurrentThread.ManagedThreadId));
Thread.Sleep(3000);
}
在这里,我确保等待所有线程。而且,我确保匹配控制台输出。 OP 代码不做的事情。
不过,时差还是有的。
让我告诉你,至少在我的测试中,有什么不同:顺序。在ParallelThread 之前运行ParallelProcess,它们都应该需要3 秒才能完成(忽略初始运行,因为编译需要更长的时间)。我真的无法解释。
我们可以进一步修改上面的代码以使用ThreadPool,这也导致ParallelProcess 在 3 秒内完成(即使我没有修改那个版本)。这是我想出的ParallelThread 和ThreadPool 的版本:
public static bool ParallelThread()
{
Stopwatch sw = new Stopwatch();
sw.Start();
var events = new ManualResetEvent[10];
for (int i = 0; i < 10; i++)
{
int x = i;
events[x] = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem
(
_ =>
{
Thread1(x);
events[x].Set();
}
);
}
for (int i = 0; i < 10; i++)
{
events[i].WaitOne();
}
sw.Stop();
Console.WriteLine(string.Format("Time in secs {0}", sw.Elapsed.Seconds));
return true;
}
private static void Thread1(int x)
{
Console.WriteLine(string.Format("Printing {0} thread = {1}", x,
Thread.CurrentThread.ManagedThreadId));
Thread.Sleep(3000);
}
注意:我们可以在事件上使用WaitAll,但在STAThread 上会失败。
你有Thread.Sleep(3000),这是我们看到的 3 秒。这意味着我们并没有真正衡量任何这些方法的开销。
所以,我决定进一步研究这个问题,为此,我提高了一个数量级(从 10 到 100)并删除了 Console.WriteLine(无论如何它正在引入同步)。
这是我的代码清单:
void Main()
{
ParallelThread();
ParallelProcess();
}
public static bool ParallelProcess()
{
Stopwatch sw = new Stopwatch();
sw.Start();
Parallel.For(0, 100, x =>
{
/*Console.WriteLine(string.Format("Printing {0} thread = {1}", x,
Thread.CurrentThread.ManagedThreadId));*/
Thread.Sleep(3000);
});
sw.Stop();
Console.WriteLine(string.Format("Time in secs {0}", sw.Elapsed.Seconds));
return true;
}
public static bool ParallelThread()
{
Stopwatch sw = new Stopwatch();
sw.Start();
var events = new ManualResetEvent[100];
for (int i = 0; i < 100; i++)
{
int x = i;
events[x] = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem
(
_ =>
{
Thread1(x);
events[x].Set();
}
);
}
for (int i = 0; i < 100; i++)
{
events[i].WaitOne();
}
sw.Stop();
Console.WriteLine(string.Format("Time in secs {0}", sw.Elapsed.Seconds));
return true;
}
private static void Thread1(int x)
{
/*Console.WriteLine(string.Format("Printing {0} thread = {1}", x,
Thread.CurrentThread.ManagedThreadId));*/
Thread.Sleep(3000);
}
ParallelThread 得到 6 秒,ParallelProcess 得到 9 秒。即使在颠倒顺序之后也是如此。这让我更加确信这是对开销的真实衡量。
添加ThreadPool.SetMinThreads(100, 100); 可将ParallelThread(请记住,此版本使用ThreadPool)和ParallelProcess 的时间缩短至3 秒。这意味着这个开销来自线程池。现在,我可以回到产生新线程的版本(修改为产生 100 并带有 Console.WriteLine 注释):
public static bool ParallelThread()
{
Stopwatch sw = new Stopwatch();
sw.Start();
var threads = new Thread[100];
for (int i = 0; i < 100; i++)
{
int x = i;
threads[i] = new Thread(() => Thread1(x));
threads[i].Start();
}
for (int i = 0; i < 100; i++)
{
threads[i].Join();
}
sw.Stop();
Console.WriteLine(string.Format("Time in secs {0}", sw.Elapsed.Seconds));
return true;
}
private static void Thread1(int x)
{
/*Console.WriteLine(string.Format("Printing {0} thread = {1}", x,
Thread.CurrentThread.ManagedThreadId));*/
Thread.Sleep(3000);
}
我从这个版本中得到一致的 3 秒(这意味着时间开销可以忽略不计,因为正如我之前所说,Thread.Sleep(3000) 是 3 秒),但是我想指出,它会留下更多的垃圾来收集而不是使用ThreadPool 或 Parallel.For。另一方面,使用Parallel.For 仍然与ThreadPool 绑定。顺便说一句,如果你想降低它的性能,减少最小线程数是不够的,你还得降低最大线程数(例如ThreadPool.SetMaxThreads(1, 1);)。
总而言之,请注意Parallel.For 更容易使用,更难出错。
调用 10 个线程似乎会更快,谁能解释一下?
产生线程很快。虽然,它会导致更多的垃圾。另外,请注意您的测试不是很好。
线程会使用系统中可用的多个处理器(以并行执行)还是仅参考 CLR 进行时间切片?
是的,他们会的。它们映射到底层操作系统线程,可以被它抢占,并将根据它们的亲和力在任何内核中运行(参见ProcessThread.ProcessorAffinity)。需要明确的是,它们不是fibers,也不是协程。