子线程操作UI触发COM异常,用Dispatcher又遇InvalidOperationException求解
解决跨线程UI操作异常及Dispatcher调用报错问题
你遇到的第一个COM异常是因为非UI线程直接操作UI控件——所有WPF/UWP的UI元素都只能由创建它们的UI线程访问,跨线程直接操作就会触发该异常。
你尝试用CoreApplication.MainView.CoreWindow.Dispatcher切换线程却触发"A method was called at an unexpected time"异常,通常是因为全局CoreView/Dispatcher在当前上下文未正确初始化(比如后台线程调用时主视图未处于活跃状态)。更可靠的方式是使用当前UI控件自身的Dispatcher,比如直接用InvertedListView.Dispatcher,它绑定到创建该控件的UI线程,不会有上下文问题。
另外你的代码还有两个潜在问题:
FileSystemWatcher的Changed事件会因文件写入的不同阶段触发多次,原flag变量是非线程安全的,多线程下读写会导致逻辑混乱;- 读取文件时直接用
File.ReadLines(...).Last(),如果文件正在被其他进程写入,会引发IO异常,需要增加重试或判断文件是否可访问。
修正后的完整代码
// 用线程安全的方式控制事件触发频率 private static readonly object _lockObj = new object(); private static bool _isProcessing = false; private void MessageReceived(string message_content) { // 用当前UI控件的Dispatcher切换到UI线程 if (InvertedListView.Dispatcher.CheckAccess()) { // 已在UI线程,直接操作 InvertedListView.Items.Add(new Message(message_content, DateTime.Now, HorizontalAlignment.Left)); } else { // 跨线程,通过Dispatcher异步调用 InvertedListView.Dispatcher.InvokeAsync(() => { InvertedListView.Items.Add(new Message(message_content, DateTime.Now, HorizontalAlignment.Left)); }); } } // 初始化FileSystemWatcher的线程 Thread thread = new Thread(() => { FileSystemWatcher watcher = new FileSystemWatcher(); watcher.Path = @"C:\Users"; watcher.Filter = "text.txt"; watcher.NotifyFilter = NotifyFilters.LastWrite; watcher.Changed += OnChanged; Debug.WriteLine("START!"); watcher.EnableRaisingEvents = true; void OnChanged(object sender, FileSystemEventArgs e) { // 用lock保证线程安全,避免重复处理 lock (_lockObj) { if (_isProcessing) return; _isProcessing = true; } try { Debug.WriteLine("OnChanged"); // 等待文件写入完成,可根据实际情况调整等待时间 Thread.Sleep(100); // 尝试读取文件,增加重试逻辑避免IO异常 string lastLine = null; int retryCount = 3; while (retryCount > 0) { try { // 用Path.Combine正确拼接路径,避免手动拼接出错 string fullPath = Path.Combine(watcher.Path, watcher.Filter); lastLine = File.ReadLines(fullPath, Encoding.UTF8).Last(); break; } catch (IOException) { retryCount--; Thread.Sleep(50); } } if (!string.IsNullOrEmpty(lastLine)) { MessageReceived(lastLine); Debug.WriteLine(lastLine); } } finally { // 无论是否成功,都重置处理标记 lock (_lockObj) { _isProcessing = false; } } } });
关键修正点说明
- 使用控件自身的Dispatcher:
InvertedListView.Dispatcher确保始终指向正确的UI线程,避免全局Dispatcher的上下文问题;用CheckAccess()判断当前是否在UI线程,减少不必要的调度。 - 线程安全的事件控制:用
lock和_isProcessing替代原来的flag,避免多线程下的竞争条件,防止重复处理事件。 - 文件读取的可靠性:用
Path.Combine正确拼接路径,增加IO异常的重试逻辑,处理文件被占用的情况。 - 调整等待时间:把
Thread.Sleep(10)改成100,给文件写入留足够时间,减少读取失败的概率。
内容的提问来源于stack exchange,提问作者unsealed1051
相关产品推荐
相关产品推荐

