如果对象已创建,并在一个线程中分配了事件,然后从另一个线程运行事件

本文关键字:事件 线程 分配 然后 运行 另一个 一个 创建 对象 如果 | 更新日期: 2023-09-27 17:56:33

如果我在主 UI 线程中创建一个对象,

然后从其他线程调用该对象中的方法,引发的任何事件是否会在单独的线程或主 UI 线程中运行?

例:

WebClient client = new WebClient();
client.DownloadDataCompleted += new DownloadDataCompletedEventHandler(
    delegate(object sender, DownloadDataCompletedEventArgs e)
    {
        Thread.Sleep(60000);
    });
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(
    delegate(object sender, DoWorkEventArgs e)
    {
        Thread.Sleep(60000);
        client.DownloadDataAsync("http://www.example.com");
    });
worker.RunWorkerAsync();

将事件与他们自己的方法而不是委托联系起来会有什么不同吗?

谢谢。

如果对象已创建,并在一个线程中分配了事件,然后从另一个线程运行事件

事件在调用它的线程上同步引发,也就是说,事件的所有订阅者都在引发事件的线程中运行。

您也可以通过 BeginInvoke 异步引发事件,在这种情况下,我相信它"最终"由应用程序线程池中的线程处理。

您需要保护与任何 UI 组件交互的任何处理程序,防止在与主 UI 线程不同的线程上执行。

您可以通过 Control.InvokeRequired/Control.Invoke(...) 技术执行此操作,请参阅

事件处理程序在调用它们的线程上运行,就像任何其他方法一样。但是,如果处理程序是在实现ISynchronizeInvoke的类(如 winforms 控件)上定义的,则可以在创建它的线程上调用它。以下是我用于引发自动处理此问题的事件的扩展方法:

    /// <summary>
    /// Fires an event and catches any exceptions raised by an event handler.
    /// </summary>
    /// <param name="ev">The event handler to raise</param>
    /// <param name="sender">The sender of the event.</param>
    /// <param name="e">Event arguments for the event.</param>
    /// <typeparam name="T">The type of the event args.</typeparam>
    public static void Fire<T>(this EventHandler<T> ev, object sender, T e) where T : EventArgs
    {
        if (ev == null)
        {
            return;
        }
        foreach (Delegate del in ev.GetInvocationList())
        {
            try
            {
                ISynchronizeInvoke invoke = del.Target as ISynchronizeInvoke;
                if (invoke != null && invoke.InvokeRequired)
                {
                    invoke.Invoke(del, new[] { sender, e });
                }
                else
                {
                    del.DynamicInvoke(sender, e);
                }
            }
            catch (TargetInvocationException ex)
            {
                ex.InnerException.PreserveStackTrace();
                throw ex.InnerException;
            }
        }
    }
    /// <summary>
    /// Called on a <see cref="TargetInvocationException"/> to preserve the stack trace that generated the inner exception.
    /// </summary>
    /// <param name="e">The exception to preserve the stack trace of.</param>
    public static void PreserveStackTrace(this Exception e)
    {
        var ctx = new StreamingContext(StreamingContextStates.CrossAppDomain);
        var mgr = new ObjectManager(null, ctx);
        var si = new SerializationInfo(e.GetType(), new FormatterConverter());
        e.GetObjectData(si, ctx);
        mgr.RegisterObject(e, 1, si);
        mgr.DoFixups();
    }

编辑:PreserveStackTrace并不是这个问题的答案的一部分,它只是我使用的解决方案的一部分。我实际上是从SO的另一个答案中得到这种方法的,我不记得确切地是从哪里来的,但它的功劳确实属于其他人。对不起,我不记得是谁了。