【问题标题】:Can this async code complete out of order?这个异步代码可以乱序完成吗?
【发布时间】:2021-10-03 04:56:27
【问题描述】:

我在 AspNet WebApi 控制器中有以下 C# 代码:

private static async Task<string> SaveDocumentAsync(HttpContent content) {
    var path = "something";
    using (var file = File.OpenWrite(path)) {
        await content.CopyToAsync(file);
    }
    return path;
}

public async Task<IHttpActionResult> Put() {
    var path = await SaveDocumentAsync(Request.Content);
    await SaveDbRecordAsync(path); // writes something to the database using System.Data and awaiting Async methods
    return OK();
}

我有时会在文档写入完成之前看到数据库记录可见。这是一个可能的执行顺序吗? (也有可能我的文件系统没有给我想要的语义)。

澄清我是如何观察到这一点的。它是一个从数据库中读取路径然后尝试读取文件并发现它不存在的应用程序。该文件确实在不久之后出现。 这不会每次都发生,通常文件首先出现。也许 1000 分之一的错误发生。

这被证明是文件系统语义。我以为我已经排除了我复制的文件系统,但我做错了。代码的行为符合预期。

【问题讨论】:

  • I am sometimes seeing the database record visible before the document has finished being written. - 解决方案资源管理器的更新可能比您的数据库晚。文件在那里,但它出现延迟,因为它没有通过 VS 立即建立索引
  • +1 @gpanagopoulos 的评论。具体来说,我已经看到File.Move() 在 docker-alpine 上的操作完成之前返回。将基础镜像从 alpine 切换到 debian 解决了这个问题。这是在 .NET Core 2.2 上的。
  • @Silvermind 我最初正在做类似的事情,但我想在问这个问题之前我已经消除了它。我正在使用 SMB 共享。这可能是客户端缓存的产物,但如果是这样的话,那就很奇怪了,因为 SMB 既有正缓存也有负缓存。消费者不应该在文件写入之前请求文件,因此该机器上的负缓存永远不会被填充。
  • 你能不能在完成HttpContent之后尝试处理它,看看是否有什么不同?
  • @TheodorZoulias 我会试试的。需要一些时间,因为这是目前唯一的现场问题...

标签: c# asp.net-web-api async-await


【解决方案1】:

当您写入 2 个不同的文件(一个文件,一个数据库)时,操作系统完全可以按照最适合存储介质的顺序执行写入。

在旋转存储的旧时代,2 个请求将在写入队列中,如果 r/w 磁头当前更靠近数据库的磁道,而不是文件,那么操作系统(或者可能是HDD 控制器)将首先写入数据库数据,然后是文件数据。

这假设您的文件和数据库服务器都在同一台物理机器上运行。如果您正在写入共享文件夹,并且/或者数据库服务器也在另一台机器上,那么谁知道他们将以什么顺序完成。

【讨论】:

  • 任务会以不同的顺序完成吗?一旦第一个任务被等待并完成,继续(方法的其余部分)执行,然后第二个任务触发。
  • 从代码的角度来看,它们会按顺序执行,但从操作系统的角度来看,数据存储可能不是。
  • @Neil 文件写入周围的 using 块包括刷新和关闭。我通过文件系统 API 观察文件,而不是通过查看磁盘上的字节。所以虽然你的答案是正确的,但它并不能解释我所看到的。
【解决方案2】:

由于您在调用 SaveDbRecordAsync 之前是 awaiting SaveDocumentAsync 函数,因此它会在 SaveDocumentAsync 完成后执行。

如果您要并行触发任务,那么await 他们:

var saveTask = SaveDocumentAsync(Request.Content);
var dbTask = SaveDbRecordAsync("a/path.ext");

await saveTask;
await dbTask;

那么你将无法保证完成顺序。

@Neiston 提出了一个很好的观点:您用于查看结果的应用程序可能会延迟更新并导致您认为顺序已切换。

【讨论】:

  • path 哪里来的?
  • 这是复制粘贴的错字。
  • 现在作为示例并没有什么意义,因为SaveDbRecordAsync 依赖于SaveDocumentAsync 的结果。
  • 是的。我应该用一个不同的例子。关键是不应该乱序执行。
猜你喜欢
  • 1970-01-01
  • 2018-04-07
  • 1970-01-01
  • 2020-12-24
  • 1970-01-01
  • 2014-03-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多