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

子线程操作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;
            }
        }
    }
});

关键修正点说明

  1. 使用控件自身的Dispatcher:InvertedListView.Dispatcher确保始终指向正确的UI线程,避免全局Dispatcher的上下文问题;用CheckAccess()判断当前是否在UI线程,减少不必要的调度。
  2. 线程安全的事件控制:用lock和_isProcessing替代原来的flag,避免多线程下的竞争条件,防止重复处理事件。
  3. 文件读取的可靠性:用Path.Combine正确拼接路径,增加IO异常的重试逻辑,处理文件被占用的情况。
  4. 调整等待时间:把Thread.Sleep(10)改成100,给文件写入留足够时间,减少读取失败的概率。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 19:50:11