英文:
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
task
builder behave this way, and is it documented anywhere? - Is it possible to get the
task
builder to move the call tolongRunningFunc
intoTask.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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论