线程安全异步方法

Thread safe async method

假设我们有以下 class:

    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第二次调用时,它不会再次上传文件,只是returns URI现有的。

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

The question is how to make this class thread safe to make sure, that the file will be uploaded only once?

您可以使用 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