views:

56

answers:

2

I need to implement in my class "Invoke()" method with the same behavior as Control.Invoke() has.

So when I am working with the instance of my InvokableEntity class from a thread different from thread where instance was created, I will be able to call invokableEntity.Invoke(delegate) and delegate will be executed in the context of the thread instance of InvokableEntity was created in.

And yes, I've read this question, it doesn't helped me =(

Please take a look at his code, it illustrates my tries to implement described behavior for event handler (CustomProcessor_ProgressChanged method should be executed from thread where it was subscribed to the event, but I can't do this):

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());
        }
    }
}

Thanks!

+2  A: 

Control.Invoke() uses the PostMessage() API call to post a message that will be consumed by the message pump of the main GUI thread.

Let's pretend that you create an instance of CustomProcessor in Thread#1 that is not a GUI thread and after creating an instance of CustomProcessor, Thread#1 goes on with a long processing operation. If you need to Invoke an operation on Thread#1, you do not want to abort the current operation, it is a better idea instead to queue a workitem that will be consumed by Thread#1 each time Thread#1 finishes a task.
If there is no logic that enqueues new work from other threads and dequeues and processes it within Thread#1, it will not magically work out-of-the-box.

If you need this functionality on multiple threads that do not have a message pump, with a custom type that does not derive from Control, a message queue or equivalent might be implemented. This way, however the creating thread will be spending its life in the message/work queue loop, most of the time waiting for new work - just like the main GUI thread in a Windows Forms application when you call Application.Run(). This might not be what you want.

andras
Thank you very much, you gave me a brilliant explanation!
Restuta
+2  A: 

Marshaling a call from one thread to another is a considerable trick. What is required is a mechanism that ensures that the target thread is idle and can run execution requests without any danger of nasty re-entrancy problems.

The SynchronizationContext class is the base class for such a mechanism. Its default implementation doesn't actually synchronize anything, it runs the delegate target on a threadpool thread.

Windows Forms provides a class that does what you are looking for, the WindowsFormsSynchronizationContext class. It relies on the message loop in the Application class to provide the synchronization. An instance of this class gets automatically installed, the Application.Run() method takes care of it. WPF has one as well, DispatcherSynchronizationContext.

Problem is: you never called Application.Run() and you're not running a message loop. The required plumbing is missing. You cannot get what you want without that message loop.

Hans Passant
Thank you too, your explanation also was very helpfull, thanks for your time!
Restuta