如何关闭卡住的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
相关产品推荐
相关产品推荐

