9

It's similar to many questions, but not rly. I need something like BeginInvoke for Winforms, but not for winforms only. So i need single method, that works for any type of application, so i'm calling

void ExecuteInMainContext(Action action)
{
   ...
}

and it should work, be called from Console, winforms, wpf and so on. All methods i saw was using BeginInvoke for winforms, Dispatcher.Invoke for WPF etc. But i should call it from library and i don't know from where it's called. And it also should be transparent to calling code, so it shouldn't pass something like pointer to calling main thread etc, lib should get this info itself from environment, not from user code and without any global variables, of course.

I've tried to use Task.ConfigureAwait, but it didn't help.

i found this one

You can't do this (without a lot of work) in a Console application. The mechanisms built into the TPL for marshaling the call back onto a thread all rely on the thread having an installed SynchronizationContext. This typically gets installed by the user interface framework (ie: Application.Run in Windows Forms, or in WPF's startup code, etc).

but I hope it's possible.

code for tests:

using System;
using System.Threading;

namespace Test
{
    class Program
    {
        private static void Main(string[] args)
        {

            Console.WriteLine("Main: " + Thread.CurrentThread.ManagedThreadId);
            Publisher publisher = new Publisher(Method);
            Console.ReadLine();
        }

        private static void Method(string s)
        {
            Console.WriteLine(s + " " + Thread.CurrentThread.ManagedThreadId);
        }

    }

    class Publisher
    {
        public event Action<string> Action;

        protected virtual void OnAction(string obj)
        {
            Action<string> handler = Action;
            if (handler != null)
            {
                SafeCall(() => handler(obj));
            }
        }

        private static void SafeCall(Action action)
        {
            // ???
            action(); // should write 1
        }

        public Publisher(Action<string> action)
        {
            Action = action;
            Console.WriteLine("Publisher thread: " + Thread.CurrentThread.ManagedThreadId);
            Thread thread = new Thread(() => OnAction("hello"));
            thread.Start();
        }
    }
}

so it should write same number anywhere.

Alex Zhukovskiy
  • 9,565
  • 11
  • 75
  • 151
  • Take a look at [SynchronizationContext](http://msdn.microsoft.com/en-us/library/system.threading.synchronizationcontext%28v=vs.110%29.aspx) and [here](http://www.codeproject.com/Articles/31971/Understanding-SynchronizationContext-Part-I) – Alberto Nov 30 '13 at 12:31
  • So it's null in my case – Alex Zhukovskiy Nov 30 '13 at 12:57

1 Answers1

10

Try this

void ExecuteInMainContext(Action action)
    {
        var synchronization = SynchronizationContext.Current;
        if (synchronization != null)
        {
            synchronization.Send(_ => action(), null);//sync
            //OR
            synchronization.Post(_ => action(), null);//async
        }
        else
            Task.Factory.StartNew(action);

        //OR
        var scheduler = TaskScheduler.FromCurrentSynchronizationContext();

        Task task = new Task(action);
        if (scheduler != null)
            task.Start(scheduler);
        else
            task.Start();
    }
yo chauhan
  • 12,079
  • 4
  • 39
  • 58
  • `task.Start(TaskScheduler.FromCurrentSynchronizationContext());` throws exception `SynchronizationContext.Current` is null – Alex Zhukovskiy Nov 30 '13 at 12:58
  • It will be null for ConsoleApplication, For that case use simple Task.Factory.StartNew(action); – yo chauhan Nov 30 '13 at 13:00
  • Code is above. In my case it writes `4`, when should write `1`. Or does it mean, that we shouldn't use any syncronization while working with console at all? – Alex Zhukovskiy Nov 30 '13 at 13:05
  • yes it will because for Console Application Task will be scheduled to any available thread in ThreadPool , but for WPF and Winform it will be 1 if Managed ID of Thread is 1 – yo chauhan Nov 30 '13 at 13:10