如何在自定义类型中实现Control.Invoke模拟?

时间:2010-04-07 09:11:41

标签: .net multithreading

我需要在我的类“ Invoke()”方法中实现与 Control.Invoke()具有相同的行为。

因此,当我从与创建实例的线程不同的线程处理我的 InvokableEntity 类的实例时,我将能够调用 invokableEntity.Invoke( delegate 委托将在创建InvokableEntity的线程实例的上下文中执行。

是的,我已经阅读了this个问题,这对我没有帮助=(

请看一下他的代码,它说明了我尝试实现事件处理程序所描述的行为( CustomProcessor_ProgressChanged 方法应该从订阅该事件的线程执行,但我不能这样做):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.ComponentModel;
using System.Windows.Forms;

namespace MultiThread
{
    class Program
    {
        private static CustomProcessor customProcessor = new CustomProcessor();

        static void Main(string[] args)
        {
            Console.WriteLine("Worker was run from thread: {0}", Thread.CurrentThread.ManagedThreadId);
            customProcessor.ProgressChanged += new EventHandler(CustomProcessor_ProgressChanged);

            Thread workerThread = new Thread(customProcessor.Process);
            AsyncOperation asyncOperation = AsyncOperationManager.CreateOperation(null);
            //SynchronizationContext context = SynchronizationContext.Current;
            workerThread.Start(asyncOperation);

            Console.ReadLine();
        }

        static void CustomProcessor_ProgressChanged(object sender, EventArgs e)
        {
            Console.WriteLine("Custom ProgressChanged was handled in thread: {0}", Thread.CurrentThread.ManagedThreadId);
        }
    }

    class CustomProcessor
    {
        public event EventHandler ProgressChanged;

        public void RaiseProcessChanged(object o)
        {
            Console.WriteLine("RaiseProgressChanged was handled in thread: {0}", Thread.CurrentThread.ManagedThreadId);
            if (this.ProgressChanged != null)
            {
                this.ProgressChanged(this, EventArgs.Empty);
            }
        }

        public void Process(object asyncOperation)
        {
            Console.WriteLine("CustomProcessor.Process method was executed in thread: {0}", Thread.CurrentThread.ManagedThreadId);

            AsyncOperation asyncOperationInternal = (AsyncOperation)asyncOperation;
            asyncOperationInternal.Post(this.RaiseProcessChanged, null);

            //SynchronizationContext context = (SynchronizationContext) asyncOperation;
            //context.Send(s => this.RaiseProcessChanged(null), null);

            //this.RaiseProcessChanged(new object());
        }
    }
}

谢谢!

2 个答案:

答案 0 :(得分:2)

Control.Invoke()使用PostMessage() API调用发布将由主GUI线程的消息泵使用的消息。

让我们假装您在CustomProcessor中创建一个Thread#1的实例,该实例不是GUI线程,在创建CustomProcessor的实例后,Thread#1会持续很长时间加工业务。如果您需要对Invoke进行Thread#1操作,则不希望中止当前操作,最好将每次将Thread#1消耗的工作项排队Thread#1完成任务 如果没有逻辑从其他线程中排列新工作并在Thread#1内出列并对其进行处理,那么它就不会神奇地开箱即用。

如果您在没有消息泵的多个线程上需要此功能,并且自定义类型不是从Control派生的,则可能会实现消息队列或等效消息。这样,创建线程将花费在消息/工作队列循环中,大多数时间等待新工作 - 就像调用Application.Run()时Windows窗体应用程序中的主GUI线程一样。这可能不是你想要的。

答案 1 :(得分:2)

从一个线程到另一个线程进行编组调用是一个相当大的伎俩。所需要的是一种机制,确保目标线程处于空闲状态,并且可以运行执行请求,而不会有任何令人讨厌的重入问题的危险。

SynchronizationContext类是此类机制的基类。它的默认实现实际上不会同步任何东西,它在线程池线程上运行委托目标。

Windows Forms提供了一个可以满足您的需求的类,即WindowsFormsSynchronizationContext类。它依赖于Application类中的消息循环来提供同步。自动安装此类的实例,Application.Run()方法负责处理它。 WPF也有一个,DispatcherSynchronizationContext。

问题是:你从未调用过Application.Run()而你没有运行消息循环。缺少所需的管道。如果没有这个消息循环,你就无法得到你想要的东西。