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

使用FileSystemWatcher读取文件遇占用错误及线程优化咨询

问题解决方案

一、搞定“文件正在使用中”错误

1. 用正确的读取姿势

大概率是读取时没放开文件共享权限,导致和robocopy或者写入文件的程序冲突。直接用带共享模式的FileStream,读完自动释放资源:

string filePath = "你的文件路径";
using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (var sr = new StreamReader(fs))
{
    string content = sr.ReadToEnd();
    // 拆分内容、插库的逻辑放这
}

using语句会自动关流释放资源,不会攥着文件不放。

2. 先复制再读,绕开robocopy冲突

robocopy每10分钟移文件,刚好撞上读取就会报错。先把文件复制到临时目录,读临时文件就不怕原文件被移动了:

string tempPath = Path.Combine(Path.GetTempPath(), Path.GetFileName(filePath));
// 复制时也加共享权限,防止原文件被占用
using (var sourceStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (var targetStream = new FileStream(tempPath, FileMode.Create, FileAccess.Write))
{
    sourceStream.CopyTo(targetStream);
}
// 读临时文件
using (var sr = new StreamReader(tempPath))
{
    string content = sr.ReadToEnd();
    // 处理逻辑
}
File.Delete(tempPath); // 读完删掉临时文件

3. 加重试机制,别一次就放弃

有时候文件只是暂时被占用,等几秒再试就行,最多重试3次:

int retryCount = 3;
int waitMs = 1000;
string content = null;
while (retryCount > 0)
{
    try
    {
        using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
        using (var sr = new StreamReader(fs))
        {
            content = sr.ReadToEnd();
            break;
        }
    }
    catch (IOException ex) when ((ex.HResult & 0x0000FFFF) == 32) // 32是文件占用的错误码
    {
        retryCount--;
        Thread.Sleep(waitMs);
    }
}
if (content == null)
{
    // 记录日志,标记这个文件读取失败
}

二、优化效率和线程控制

1. 批量处理+批量插库

单个文件处理完再搞下一个太浪费时间,先把所有待处理文件的内容读出来,攒一批再插数据库,减少数据库连接的开销。还可以控制并行线程数,别把服务器搞崩:

string[] files = Directory.GetFiles("你的监控文件夹");
var dataList = new ConcurrentBag<YourDataModel>(); // 线程安全的集合存数据

// 控制最多4个线程并行处理,根据服务器配置调整
Parallel.ForEach(files, new ParallelOptions { MaxDegreeOfParallelism = 4 }, filePath =>
{
    // 用上面的安全方法读取文件内容
    string content = ReadFileSafely(filePath);
    if (content == null) return;
    // 拆分内容成你的数据模型
    var data = SplitContentToModel(content);
    dataList.Add(data);
});

// 批量插库,用SqlBulkCopy比逐条Insert快N倍
using (var conn = new SqlConnection("你的数据库连接字符串"))
{
    conn.Open();
    using (var bulkCopy = new SqlBulkCopy(conn))
    {
        bulkCopy.DestinationTableName = "目标表名";
        // 映射列名,比如你的模型属性对应数据库列
        bulkCopy.ColumnMappings.Add("Data1", "DB_Column1");
        bulkCopy.ColumnMappings.Add("Data2", "DB_Column2");
        // 把集合转成DataTable(或者直接用IEnumerable,.NET Core 3.0+支持)
        var dataTable = ConvertToDataTable(dataList);
        bulkCopy.WriteToServer(dataTable);
    }
}

2. 用文件监控代替定时轮询

如果之前是定时扫文件夹,换成FileSystemWatcher实时监控文件创建事件,不用白耗资源轮询:

var watcher = new FileSystemWatcher("你的监控文件夹");
watcher.Created += (sender, e) =>
{
    // 注意:文件刚创建时可能还在写入,等半秒再处理,或者用重试逻辑
    Thread.Sleep(500);
    ProcessFile(e.FullPath); // 这里调用你的文件处理方法
};
watcher.EnableRaisingEvents = true;

3. 复用数据库连接

别每次处理一个文件就新建连接,默认的连接池会自动管理连接,用using语句就行,不用手动关,连接池会把连接回收复用。

三、额外提醒

  • 处理完的文件要么重命名,要么移到“已处理”文件夹,别重复处理。
  • 加日志记录,比如哪个文件读失败了、插库报错了,方便排查问题。
  • 测试时手动用robocopy移文件,看看还会不会出占用错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 08:35:56