我有以下虚拟代码来测试F#中的TPL。(Mono 4.5,Xamarin Studio,四核MacBook Pro)
让我惊讶的是,所有进程都在同一线程上完成。根本没有并行性。
open System
open System.Threading
open System.Threading.Tasks
let doWork (num:int) (taskId:int) : unit =
for i in 1 .. num do
Thread.Sleep(10)
for j in 1 .. 1000 do
()
Console.WriteLine(String.Format("Task {0} loop: {1}, thread id {2}", taskId, i, Thread.CurrentThread.ManagedThreadId))
[<EntryPoint>]
let main argv =
let t2 = Task.Factory.StartNew(fun() -> doWork 10 2)
//printfn "launched t2"
Console.WriteLine("launched t2")
let t1 = Task.Factory.StartNew(fun() -> doWork 8 1)
Console.WriteLine("launched t1")
let t3 = Task.Factory.StartNew(fun() -> doWork 10 3)
Console.WriteLine("launched t3")
let t4 = Task.Factory.StartNew(fun() -> doWork 5 4)
Console.WriteLine("launched t4")
Task.WaitAll(t1,t2,t3,t4)
0 // return an integer exit code
然而,如果我将线程休眠时间从10ms增加到100ms,我可以看到一点并行性。
我做错了什么?这是什么意思?我确实考虑过CPU在TPL启动新线程的任务之前完成工作的可能性。但是对我来说这没有意义。我可以将内部虚拟循环`for j in 1 .. 1000 do ()`的循环次数增加1000次。结果是相同的:没有并行性(`thread.sleep`设置为10ms)。
另一方面,在C#中相同的代码则产生了期望的结果:所有任务以混合顺序(而不是顺序执行)将消息打印到窗口。
更新:
如建议所示,我将内部循环更改为执行一些“实际”的操作,但结果仍然是在单个线程上执行。
更新2:
我不太理解Luaan的评论,但我刚刚在朋友的电脑上进行了测试。使用相同的代码,多线程并行运行(无需线程休眠)。看起来是与Mono有关。但是Luaan能再次解释一下我应该从TPL期望什么吗?如果我有要并行执行并利用多核CPU的任务,那么TPL难道不是正确的选择吗?
更新3:
我再次尝试了@FyodorSoikin的建议,使用不会被优化掉的虚拟代码。不幸的是,工作量仍然无法使Mono TPL使用多个线程。目前我唯一能让Mono TPL分配多个线程的方法是强制现有线程休眠超过20ms。我没有足够的资格断言Mono是错误的,但我可以确认相同的代码(相同的基准工作量)在Mono和Windows下具有不同的行为。
Thread
而不是Task
。任务的本质更多是异步而不是并行的。你不能确定它们将在许多线程中执行。 参见:https://dev59.com/jmYr5IYBdhLWcg3wvspA - pizyckiThread.SpinWait(1000000)
时才开始看到一些执行时间,大约为5毫秒 - 在现代CPU上,1000太低了。 - Luaanfact
函数没有副作用,并且你忽略了它的返回值。因此,优化掉它是完全可以的。你需要做一些编译器不知道如何优化的事情。请参考我的答案。 - Fyodor Soikin