F#:调用Task.WhenAny?时避免出现竞争情况。

时间:2019-10-19 06:53:37

标签: asynchronous f#

因此,在使用F#的Async模型进行了很多工作之后,我想我对此深有感触,并了解了它与C#的Task模型的区别。我最喜欢它们互操作方式的一件事是Async.AwaitTask

...
public Task<int> SomeCsharpApiAsync() {
    ...
}
...
let someFsharpAsyncCode = async {
    let! someResult = Async.AwaitTask (SomeClass.SomeCsharpApiAsync())
    return someResult
}

使用上面的代码,调用AwaitTask的行不仅等待任务完成,而且在我认为是原子操作的操作中将其结果分配给变量someResult。非常整洁!

但是,在使用Task.WhenAny时,我在实现相同目的(原子性,因此缺乏比赛条件)方面遇到问题:

...
public Task<int> SomeCsharpApiAsync(string someParam) {
    ...
}
...
let someFsharpAsyncCode = async {
    let task1 = SomeClass.SomeCsharpApiAsync "foo"
    let task2 = SomeClass.SomeCsharpApiAsync "bar"
    let! fastestTask = Async.AwaitTask (Task.WhenAny([task1;task2]))
    return fastestTask.Result
}

与上面的代码有关的问题是AwaitTask操作返回的是任务(最快),而不是结果,因此它是间接的,请稍后再调用.Result来解决。但是我发现了有关此问题的问题,我认为其罪魁祸首是比赛条件:最快的任务可能在此时产生一些结果,但是在检索结果时,可能已请求(取消了)任务的cancelledToken!还是最快的任务更快,因为实际上取消的更快?我不确定,我对如何弄清楚这里发生的事情有些迷惑。

也许我可以通过使用F#替代C#的Task.WhenAny来完全避免该问题?

1 个答案:

答案 0 :(得分:0)

查看此Async.WhenAny实现是否可以解决您的问题: https://github.com/tpetricek/TryJoinads/blob/master/src/FSharp.Joinads/Async.fs#L9

另请参见此博客:http://tomasp.net/blog/joinads-async-implement.aspx/

此示例与您的代码非常相似,但也许会有细微的差别:https://github.com/microsoft/fsharplu/blob/master/FSharpLu/Async.fs#L59

这是另一个具有不同用途的简短示例,但可能仍会有所帮助: http://www.fssnip.net/hx/title/AsyncAwaitTask-with-timeouts