英文:
Code inside a task expression is executed before the task is created
问题
考虑以下代码:
let makeTask n =
task { return longRunningFunc n }
假设花括号内的所有内容都是任务的一部分,我期望这段代码等同于:
let makeTask n =
Task.Run(fun () -> longRunningFunc n)
但实际上它等同于:
let makeTask n =
let value = longRunningFunc n
Task.Run(fun () -> value)
这既出乎意料,也不太理想,特别是在尝试并行运行多个这样的任务时:
let myTask =
Array.init 100 makeTask // 错误:对 longRunningFunc 的调用是顺序执行的
|> Task.WhenAll
请注意,async 构造器的行为符合预期。我知道任务是“热”的,而 F# 的异步是“冷”的,但我认为这并不能解释这个差异。
我的问题:
- 为什么
task构造器会有这种行为,有没有相关的文档说明? - 是否有可能让
task构造器按预期将对longRunningFunc的调用移到Task.Run中?
英文:
Consider the following:
let makeTask n =
task { return longRunningFunc n }
Under the assumption that everything inside the curly braces is part of the task, I would expect this to be equivalent to:
let makeTask n =
Task.Run(fun () -> longRunningFunc n)
But it is apparently actually equivalent to:
let makeTask n =
let value = longRunningFunc n
Task.Run(fun () -> value)
Which is both unexpected and far less desirable, for example, when attempting to run many such tasks in parallel:
let myTask =
Array.init 100 makeTask // oops: calls to longRunningFunc occur sequentially
|> Task.WhenAll
Note that the async builder behaves as expected. I understand that tasks are "hot", while F# asyncs are "cold", but I don't think that explains this discrepancy.
My questions:
- Why does the
taskbuilder behave this way, and is it documented anywhere? - Is it possible to get the
taskbuilder to move the call tolongRunningFuncintoTask.Run, as expected?
答案1
得分: 0
F#编译器团队表示这是正确的行为。在任务表达式的顶部执行一个虚拟任务(例如do! Task.Delay(1))可以修复这个问题,但并不十分优雅。嗯,好吧。
英文:
The F# compiler team says this is the correct behavior. Executing a dummy task at the top of the task expression (e.g. do! Task.Delay(1)) would fix the problem, but isn't very elegant. Oh well.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论