You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FileStream创建文件时Sharing violation IOException问题排查与解决

问题原因分析

共享冲突IOException的原因

虽然每个线程操作的是不同文件名,但同一目录的元数据(如目录项、文件分配表)是共享资源。当多个线程同时在该目录下创建文件时,Windows系统需要更新目录的元数据,这个过程如果没有同步,会触发系统级的共享冲突,抛出IOException: Sharing violation。这种情况和单个文件的读写冲突无关,而是目录层面的操作竞态导致的。

ReaderWriterLockSlim导致死锁的原因

ReaderWriterLockSlim是线程绑定的锁,它要求获取锁和释放锁必须在同一个线程上。但你的代码中包含异步操作(如await _httpClient.GetAsync、await response.Content.CopyToAsync),await会释放当前线程,后续代码恢复时可能在另一个线程执行。这会导致:

  • 锁被持有在原线程,但后续的ExitWriteLock在新线程执行,无法正确释放锁,导致其他线程永久等待锁,引发死锁。
  • 锁的持有时间被无限拉长(包含HTTP请求的耗时),大幅增加了死锁的概率。
解决方案

1. 使用支持异步的锁:SemaphoreSlim

替换ReaderWriterLockSlim为SemaphoreSlim,它支持异步场景,且不与线程绑定。示例代码如下:

// 静态声明,限制同时操作目录的线程数,设为1确保串行创建文件
private static readonly SemaphoreSlim _directoryLock = new SemaphoreSlim(1, 1);

public async Task ProcessFileAsync(string url, string pathToNewFile, string name, string filepath, string directoryName)
{
    CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
    HttpResponseMessage response = null;
    try
    {
        var uri = new Uri(url);
        response = await _httpClient.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead, cancellationTokenSource.Token);
        response.EnsureSuccessStatusCode();

        // 仅在创建文件时加锁,缩小锁范围
        await _directoryLock.WaitAsync(cancellationTokenSource.Token);
        try
        {
            await using (var fs = new FileStream(Path.Combine(pathToNewFile, $"{name}.zip"), FileMode.Create))
            {
                await response.Content.CopyToAsync(fs, cancellationTokenSource.Token);
            }
        }
        finally
        {
            _directoryLock.Release();
        }

        // 读取和解压操作不需要锁,文件已完全写入且文件名唯一
        using (var stream = File.OpenRead(filepath))
        using (var zipArchive = new ZipArchive(stream, ZipArchiveMode.Read))
        {
            if (!string.IsNullOrEmpty(directoryName) && !Directory.Exists(directoryName))
            {
                Directory.CreateDirectory(directoryName);
            }
            zipArchive.ExtractToDirectory(directoryName, true);
        }
    }
    catch (Exception ex)
    {
        taskCompletionSource?.TrySetException(ex);
    }
    finally
    {
        response?.Dispose();
        cancellationTokenSource?.Dispose();
    }
}

2. 优化锁的范围

只对目录下创建文件的操作加锁,HTTP请求、文件读取解压等操作不需要锁,这样可以大幅缩短锁的持有时间,减少线程阻塞的概率。

3. 使用更安全的文件创建API(可选)

在.NET Core 3.0+或.NET 5+中,可以使用File.CreateAsync替代FileStream,它是异步原生API,可能进一步降低系统层面的冲突概率:

await using (var fs = await File.CreateAsync(Path.Combine(pathToNewFile, $"{name}.zip"), cancellationTokenSource.Token))
{
    await response.Content.CopyToAsync(fs, cancellationTokenSource.Token);
}

内容的提问来源于stack exchange,提问作者Emil

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 15:27:35