1

I'm having trouble invoking an event from a secondary thread in the main thread. The event handler is not executed on main thread. Can anyone give me some pointers on what I'm doing wrong.

Thanks

namespace ThreadSyncExample
{
  class Program
  {
    static void Main(string[] args)
    {
      Console.WriteLine("MainThread: " + System.Threading.Thread.CurrentThread.ManagedThreadId);

      Execute execThe = new Execute();
      execThe.FinishedThread += (src, arg) =>
      {
        //This shoould be executed on MainThread right?
        Console.WriteLine("Thread Id: " + System.Threading.Thread.CurrentThread.ManagedThreadId);
      };

      execThe.Run();
      Console.ReadKey();
    }

  }


  class Execute
  {
    public void Run()
    {
      Thread exec = new Thread(() =>
      {
        Console.WriteLine("Worker Thread : " + System.Threading.Thread.CurrentThread.ManagedThreadId);
        OnFinishedThread();
      });

      exec.Start();
    }

    public event EventHandler FinishedThread;
    protected virtual void OnFinishedThread()
    {
      if (null != FinishedThread)
      {
        EventArgs args = new EventArgs();
        FinishedThread(this, EventArgs.Empty);
      }
    }
  }
}
Ciprian
  • 3,533
  • 1
  • 18
  • 22
  • Are you getting an exception or is it just not working? – LukeHennerley Oct 03 '12 at 13:09
  • I'm not getting an exception, but the event handler is not executed on the main thread. – Ciprian Oct 03 '12 at 13:11
  • 1
    I don't quite fallow why you expect this event handler to be executed on main thread. You are not synchronizing it in any way. – Rafal Oct 03 '12 at 13:18
  • 1
    OnFinishedThread is executed on the secondary thread,there is no way the primary thread id will be displayed – Prabhu Murthy Oct 03 '12 at 13:21
  • 1
    possible duplicate of [Synchronous Event Handler thread execution in non-Win Form C# app](http://stackoverflow.com/questions/10444668/synchronous-event-handler-thread-execution-in-non-win-form-c-sharp-app) – Hans Passant Oct 03 '12 at 13:43

2 Answers2

2

C# events are basically just an easy-to-use collection of delegates and "firing" an event just causes the runtime to loop through all of the delegates and fire them one at a time.

So your OnFinishedThread event handler is getting called on the Worker thread.

If you want your event on the main thread, you have to Invoke() it.

EDIT :

It appears that you don't have access to forms, or WPF (so you don't have access to Invoke() either)

So you have to manually marshall the call to the main thread by thread synchronization process. It's generally a pain.

Probably the easiest solution would be to simply use a BackgroundWorker because this way you no longer need to manualy marshal the calls to the main thread.

var worker = new BackgroundWorker();
worker.DoWork += (sender, e) =>
{
    // call the XYZ function
    e.Result = XYZ();
};
worker.RunWorkerCompleted += (sender, e) =>
{
    // use the result of the XYZ function:
    var result = e.Result;
    // Here you can safely manipulate the GUI controls
};
worker.RunWorkerAsync();
Nicolas Voron
  • 2,916
  • 1
  • 21
  • 35
  • Can you elaborate a bit? Maybe some code sample. I'm kind of a newbie on the whole c# threading. – Ciprian Oct 03 '12 at 13:24
  • @Ciprian Keep in mind that most of the time events aren't *supposed* to fire in the main thread. You should assume that the event handler that you attach to an event should run in any thread that it wants. If it needs to run in the main thread usually the event handler will run the code, after being called in another thread, to go back to the main thread. The primary exception to this guideline is certain events in UI related classes for which the event needs to be in the UI thread often enough it's worthwhile to always switch. – Servy Oct 03 '12 at 13:44
  • 1
    @NicolasVoron BackgroundWorker is specifically designed for working in a UI context. I don't believe it will be able to work as intended in a console application. – Servy Oct 03 '12 at 13:52
  • @Servy, you're right, tanks for point me that out. MSDN documentation isn't very explicit about that – Nicolas Voron Oct 03 '12 at 14:07
1

the FinishedThread() event handler will be executed on the same thread as Execute.Run() is executed on. Just because you defined the body of the FinishedThread event handler in main() doesn't mean that main() somehow defines it's execution context.

Some mechanisms you can use to perform thread marshalling:

  1. Use a system.windows.forms.control and use the Invoke method to marshal a function call back to the thread the control was created on. Under the hood, this will use features of the Windows Message Loop to handle the actual marshal
  2. Use synchronization primitives to handle the marshalling manually.

Rather than re-iterate what has already been stated, check this answer for more information on marshalling:

Marshall to a thread manually

Community
  • 1
  • 1
pdriegen
  • 2,019
  • 1
  • 13
  • 19
  • 1. Unfortunately I cannot use forms. 2. Can you elaborate a bit. I'm kind of a newbie on this. – Ciprian Oct 03 '12 at 13:23
  • @Ciprian If you aren't using forms or any other UI then chances are you don't need to be running in the main thread to begin with. – Servy Oct 03 '12 at 13:45