StackExchange.Redis异步调用挂起

6

尝试找出这段代码为什么会卡住。我可以删除测试底部的任何一行,它就不会卡住,但是三行都在一起就会卡住。非常感谢任何帮助!

[Fact]
public async Task CanAddValuesInParallel() {
    var muxer = ConnectionMultiplexer.Connect("localhost");
    var db = muxer.GetDatabase();

    await AddAsync(db, "test", "1");
    await db.KeyDeleteAsync("test");

    Task.Run(() => AddAsync(db, "test", "1")).Wait();
}

public async Task<bool> AddAsync(IDatabase db, string key, string value) {
    return await db.StringSetAsync(key, value, null, When.NotExists);
}

为什么要使用 Task.Run,以及为什么要使用 Wait() - i3arnon
1
我认为问题出在 Task.Run(() => AddAsync(db, "test", "1")).Wait(); 这里。这里发生了死锁。 - Hamlet Hakobyan
这是我代码的简化版本。尽可能地将其简化。我试图理解正在发生的事情。 - Eric J. Smith
@EricJ.Smith:xUnit有一个坏习惯,为其所有测试方法提供SynchronizationContext。当混合同步和异步代码时,这可能会导致许多问题。我怀疑您正在看到我在两篇博客文章中描述的一些问题的组合此处此处 - Stephen Cleary
我还没有尝试过async void,只尝试了async Task,因为我在使用async void单元测试时遇到了问题:D - Blake Niemyjski
显示剩余3条评论
1个回答

13

我觉得这是因为混合使用了Waitawait导致了同步上下文死锁。这就是为什么你决不应该这样做 -(转为“吉尔伯特和沙利文”):好吧,几乎从不!

如果有帮助的话,我怀疑Wait子树中删除await将修复它 - 这应该很简单,因为那个子树可以被一个微不足道的通过替换。

public Task<bool> AddAsync(IDatabase db, string key, string value) {
    return db.StringSetAsync(key, value, null, When.NotExists);
}

这里的重点是SE.Redis在内部绕过了同步上下文(这是库代码的正常情况),因此不应该出现死锁。

但最终:混合使用Waitawait不是一个好主意。除了死锁之外,这也是“同步超异步”的反模式。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接