【问题标题】:Thread safe async method线程安全的异步方法
【发布时间】:2021-11-06 00:32:37
【问题描述】:

假设我们有以下类:

    public class StreamDocumentRepresentation: DocumentRepresentation
    {
        private readonly IFileStorageService _fileStorageService;

        private readonly MemoryStream _documentStream;
        private string _fileName;

        public StreamDocumentRepresentation(MemoryStream documentStream, IFileStorageService fileStorageService)
        {
            _documentStream = documentStream;
            _fileStorageService = fileStorageService;
        }

        public override async Task<string> GetDocumentUriAsync()
        {
            if (string.IsNullOrWhiteSpace(_fileName))
            {
                await UploadDocument();
            }

            return _fileName;
        }

        private async Task UploadDocument()
        {
            _fileName = $"{Guid.NewGuid()}.pdf";
            await _fileStorageService.UploadFile(_fileName, _documentStream);
        }
    }

它将文档表示为内存流,并在我们想要获取文件的 URI 时将其上传到文件存储。当我们上传文件时,fileName(也是URI)被创建并存储,所以当GetDocumentUriAsync被第二次调用时,它不会再次上传文件,而只是返回现有文件的URI。

问题是如何让这个类线程安全以确保文件只会上传一次?

【问题讨论】:

  • 锁定 if 块。
  • @JohnathanBarclay 在锁定部分中不允许使用 await 关键字

标签: c# .net multithreading parallel-processing


【解决方案1】:

问题是如何让这个类线程安全以确保文件只会被上传一次?

您可以使用SemaphoreSlim 作为一种异步兼容锁:

public class StreamDocumentRepresentation: DocumentRepresentation
{
  private readonly SemaphoreSlim _mutex = new(1);
  private readonly IFileStorageService _fileStorageService;
  private readonly MemoryStream _documentStream;
  private string _fileName;

  public override async Task<string> GetDocumentUriAsync()
  {
    await _mutex.WaitAsync();
    try
    {
      if (string.IsNullOrWhiteSpace(_fileName))
        await UploadDocument();
      return _fileName;
    }
    finally
    {
      _mutex.Release();
    }
  }

  private async Task UploadDocument()
  {
    var fileName = $"{Guid.NewGuid()}.pdf";
    await _fileStorageService.UploadFile(fileName, _documentStream);
    _fileName = fileName;
  }
}

旁注:我稍微修改了UploadDocument,以便在上传失败时它不会设置_fileName

【讨论】:

  • 我还会在 try/finally 块之前添加if (!string.IsNullOrWhiteSpace(_fileName)) return _fileName;。而且,也许,用 maxCount 初始化 SemaphoreSlim 等于 1。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-27
相关资源
最近更新 更多