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

如何关闭卡住的EXIFTOOL进程?或强制Task.WhenAll完成?

解决方案

1. 修复ExifTool进程的死锁与阻塞问题

核心问题是同步读取StandardOutput可能触发死锁——即使未显式重定向stderr,ExifTool处理异常文件时仍可能向stderr写入内容,导致缓冲区溢出,进程挂起无法退出。必须同时异步读取stdout和stderr,并调整进程等待逻辑:

修改getExifDataViaTool方法如下:

public static Dictionary<string, string> getExifDataViaTool(string sourceFile, string parameters = "-a -api largefilesupport=1")
{
    var result = new Dictionary<string, string>();
    // 用原生方式包裹路径,替代易出错的正则转义
    var argumentsString = $"{parameters} \"{sourceFile}\"";
    string ExifToolPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "FFMPEG\\exiftool.exe");

    using (var process = new Process())
    {
        var startInfo = new ProcessStartInfo
        {
            FileName = ExifToolPath,
            Arguments = argumentsString,
            RedirectStandardOutput = true,
            RedirectStandardError = true, // 必须重定向stderr,避免缓冲区满导致挂起
            UseShellExecute = false,
            CreateNoWindow = true, // 直接不创建窗口,避免残留
            WindowStyle = ProcessWindowStyle.Hidden
        };
        process.StartInfo = startInfo;

        // 异步读取输出,避免同步读取的死锁
        var outputTask = process.StandardOutput.ReadToEndAsync();
        var errorTask = process.StandardError.ReadToEndAsync();

        process.Start();

        // 设置10秒超时,超时后强制终止进程及其子进程
        if (!process.WaitForExit(10000))
        {
            try
            {
                process.Kill(true); 
                process.WaitForExit(); // 等待进程彻底退出
            }
            catch { /* 忽略终止异常,比如进程已提前退出 */ }
            Debug.WriteLine($"文件处理超时:{sourceFile}");
            return result;
        }

        // 读取输出结果
        string output = await outputTask;
        // string error = await errorTask; // 如需排查问题,可记录错误内容

        string[] lines = output.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
        foreach (var line in lines)
        {
            var pair = line.Split(new[] { ':' }, 2);
            if (pair.Length >= 2)
            {
                var key = pair[0].TrimEnd();
                var value = pair[1].TrimStart();
                result[key] = value;
                Debug.WriteLine($"{key} : {value}");
            }
        }
    }
    return result;
}

关键调整点:

  • 用using包裹Process,确保资源自动释放
  • 异步读取stdout和stderr,避免同步读取的死锁场景
  • 优先等待进程退出,再读取输出内容
  • 用Kill(true)强制杀死进程及其子进程,确保彻底终止
  • 简化路径转义逻辑,避免正则出错

2. 给单个元数据处理任务添加超时

单个文件处理卡住会导致Task.WhenAll整体停滞,给每个任务添加超时控制:

修改按钮点击事件中的任务创建逻辑:

metaTasks.Add(Task.Run(async () =>
{
    using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(15)); // 15秒超时
    try
    {
        return await Task.Run(() => _viewModel.ProcessMetaData(file), cts.Token);
    }
    catch (OperationCanceledException)
    {
        Debug.WriteLine($"文件处理超时:{file}");
        // 返回标记错误的模型,示例字段需根据实际定义调整
        return new VideoFileModel { VideoPath = file, IsCorrupted = true };
    }
}));

3. 额外优化建议

  • 日志记录:给异常文件添加详细日志,记录文件名和处理状态,方便后续排查损坏文件
  • 并发限流:用SemaphoreSlim限制同时处理的文件数量,避免系统资源耗尽
    var semaphore = new SemaphoreSlim(5); // 同时最多处理5个文件
    metaTasks.Add(Task.Run(async () =>
    {
        await semaphore.WaitAsync();
        try
        {
            // 处理逻辑
        }
        finally
        {
            semaphore.Release();
        }
    }));
    
  • 参数优化:给ExifTool添加-q参数减少冗余输出,或指定仅读取需要的元数据字段,提升处理速度

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 16:34:58