如何解决跨线程操作无效异常,以便我们可以保持已实现的代码不变

本文关键字:我们 实现 代码 解决 何解决 异常 无效 操作 线程 | 更新日期: 2023-09-27 18:30:31

当我想更新在引擎盖下的另一个线程中调用的处理程序函数中的 gui 小部件的值时:

    private void handle_PingMessage(Dictionary<string, object> msg)
    {
        string received_text = (string)msg["text"];
        label1.Text = received_text;
    }

。它引发以下异常:

Additional information: Cross-thread operation not valid: Control 'label1' accessed from a thread other than the thread it was created on.

如果我将用法更改为:

    private void handle_PingMessage(Dictionary<string, object> msg)
    {
        string received_text = (string)msg["text"];
        if (label1.InvokeRequired)
        {
            label1.Invoke(new MethodInvoker(delegate { label1.Text = received_text; }));
        }
        // do any other work here
    }

它按预期工作。

我正在手动处理事件触发过程,如下所示:

                        string event_name = "event_" + key;
                        EventInfo handler_event = this.GetType().GetEvent(event_name);
                        var event_delegate = (MulticastDelegate)this.GetType().GetField(event_name, 
                            BindingFlags.Instance | BindingFlags.NonPublic).GetValue(this);
                        foreach (var handler in event_delegate.GetInvocationList())
                        {
                            handler.Method.Invoke(handler.Target, new object[] { ((JObject)payload_dict[key]).ToObject<Dictionary<string, object>>() });
                        }

是否与手动事件触发代码部分有关,以便让用户以某种方式获得更清晰的用法:

    private void handle_PingMessage(Dictionary<string, object> msg)
    {
        string received_text = (string)msg["text"];
        label1.Text = received_text;
    }

编辑

此示例用法的完整源代码在此处。

编辑-2

解决方案后完全工作提交在这里。

如何解决跨线程操作无效异常,以便我们可以保持已实现的代码不变

在对象的构造函数中获取 SynchronizationContext.Current 的值,然后在触发事件时将事件调用发布到该同步上下文:

public class YourPingClass
{
    private SynchronizationContext syncContext;
    public YourPingClass()
    {
        syncContext = SynchronizationContext.Current ?? 
            new SynchronizationContext();
    }
    private void FireEvent()
    {
        string event_name = "event_" + key;
        EventInfo handler_event = this.GetType().GetEvent(event_name);
        var event_delegate = (MulticastDelegate)this.GetType().GetField(event_name, 
            BindingFlags.Instance | BindingFlags.NonPublic).GetValue(this);
        foreach (var handler in event_delegate.GetInvocationList())
        {
            syncContext.Post(_ => handler.Method.Invoke(handler.Target, 
                new object[] { ((JObject)payload_dict[key]).ToObject<Dictionary<string, object>>() }));
        }
    }
}

如果需要,您可能还希望允许你的类型的用户通过方法或属性资源库显式提供同步上下文(或等效项),但如果用户要从 UI 线程创建对象(这是此类自定义 UI 组件的典型特征),那么你可以自己捕获当前同步上下文,这样他们就不需要显式执行任何操作。