【问题标题】:Call event from original thread?从原始线程调用事件?
【发布时间】:2010-04-08 12:54:45
【问题描述】:

这是我的问题,我有一个类,它有一个抛出事件的对象,在这个事件中,我从我的类中抛出一个自定义事件。但不幸的是,原始对象从另一个线程抛出事件,所以我的事件也被抛出另一个线程。当我的自定义事件尝试从控件访问时,这会导致异常。

这是一个代码示例,可以更好地理解:

class MyClass
{
    // Original object
    private OriginalObject myObject;

    // My event
    public delegate void StatsUpdatedDelegate(object sender, StatsArgs args);
    public event StatsUpdatedDelegate StatsUpdated;

    public MyClass()
    {
        // Original object event
        myObject.DoSomeWork();
        myObject.AnEvent += new EventHandler(myObject_AnEvent);
    }

    // This event is called on another thread while myObject is doing his work
    private void myObject_AnEvent(object sender, EventArgs e)
    {
        // Throw my custom event here
        StatsArgs args = new StatsArgs(..........);
        StatsUpdated(this, args);
    }
}

因此,当我在 Windows 窗体上调用时,尝试从事件 StatsUpdated 更新控件时,我得到一个跨线程异常,因为它已在另一个线程上调用。

我想要做的是在原始类线程上抛出我的自定义事件,以便可以在其中使用控制。

谁能帮帮我?

【问题讨论】:

    标签: c# events multithreading


    【解决方案1】:

    您可以查看InvokeRequired/Invoke 模式。

    在尝试更新某些控件之前,请检查是否需要调用并使用 Invoke 方法,该方法将负责编组对创建此控件的线程的调用:

    Control ctrlToBeModified = // 
    if (ctrlToBeModified.InvokeRequired)
    {
        Action<Control> del = (Control c) => 
        { 
            // update the control here
        };
        ctrlToBeModified.Invoke(del, ctrlToBeModified);
    }
    

    更新:

    private void myObject_AnEvent(object sender, EventArgs e)
    {
        // Throw my custom event here
        StatsArgs args = new StatsArgs(..........);
        Control control = // get reference to some control maybe the form or 'this'
        if (control.InvokeRequired)
        {
            Action<Control> del = (Control c) => 
            { 
                // This will invoke the StatsUpdated event on the main GUI thread
                // and allow it to update the controls
                StatsUpdated(this, args);
            };
            control.Invoke(del);
        }
    }
    

    【讨论】:

    • Invoke 是一种可以从控件调用的方法,这里我要做的是直接将我的事件扔到原始线程上(来自我的类​​)。所以在我的 Windows 窗体中,我不必关心调用。我可以直接做类似“progressbar.value = arg.progress;
    • 在这种情况下,当您调用事件(未在您的 sn-p 中指定)时,您会检查是否需要调用,并在委托中调用事件,该事件已暂停到 Invoke 方法。
    • 我不太明白,你说的是我的事件还是 originalObject 事件?
    • 我说的是StatsUpdated 事件,因为这是我认为更新 UI 的事件。因此,不要在myObject_AnEvent 方法中直接调用StatsUpdated(this, args);,而是将此调用放入Control.Invoke。
    • 你能给我一个样品吗?我不明白该怎么做 Control 没有适合我的 Invoke() 方法。
    【解决方案2】:

    我不确定是否是这种情况,但如果安全的话,您可以添加到表单构造函数中:

    Control.CheckForIllegalCrossThreadCalls= false;
    

    这是仅适用于某些有限场景的“简单方法”。在我的情况下,它就像一个魅力。

    【讨论】:

    • 是的,但这也是在 windows 窗体上使用的解决方案,我想在我的课堂上纠正我的问题,然后再抛出我的自定义事件。所以我让我的 DLL 尽可能简单,以便在 Windows 窗体中使用。
    【解决方案3】:

    您似乎需要查看 SynchronizationContext 或 AsyncOperation 类。

    http://www.codeproject.com/KB/cpp/SyncContextTutorial.aspx

    【讨论】:

    • 是的,我目前正在阅读这篇文章,它似乎正是我想要的。
    • 我刚刚尝试了本文中的两种方法,但在更新进度条时仍然出现跨线程错误。这很奇怪,因为该方法看起来很合乎逻辑。
    猜你喜欢
    • 2018-01-21
    • 1970-01-01
    • 1970-01-01
    • 2014-03-02
    • 1970-01-01
    • 1970-01-01
    • 2013-04-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多