简体   繁体   中英

Windows Service invoke method from main thread

I want to excecute a method from the main thread while in the context of a worker thread and I have no clue how to do it.

In detail:

  1. Main Serivce started
  2. Worker Thread started
  3. Inside the worker thread I want to invoke a method, but not inside the worker thread context

Can somebody give me a hint how to perform this?

If I udnerstand your question correctly you normally send work to theads of another context by grabbing that thread's SycnhronizationContext and then callin Post or Send on it to give it the work. The trouble is that in console apps and windows services the default SycnhronizationContext is associated with all the thread pool threads so the work you send it can run on any of the threads.

However Stephe Toub has an example of how to create a custom SycnhronizationContext to run on a particular thread and then when you send it work it will be guaranteed to run on that thread. I've pasted some of the code into this answer for clarity.

/// <summary>Provides a pump that supports running asynchronous methods on the current thread.   </summary>
public static class AsyncPump
{
    /// <summary>Runs the specified asynchronous function.</summary>
    /// <param name="func">The asynchronous function to execute.</param>
    public static void Run(Func<Task> func)
    {
        if (func == null) throw new ArgumentNullException("func");

        var prevCtx = SynchronizationContext.Current;
        try
        {
            // Establish the new context
            var syncCtx = new SingleThreadSynchronizationContext();
            SynchronizationContext.SetSynchronizationContext(syncCtx);

            // Invoke the function and alert the context to when it completes
            var t = func();
            if (t == null) throw new InvalidOperationException("No task provided.");
            t.ContinueWith(delegate { syncCtx.Complete(); }, TaskScheduler.Default);

            // Pump continuations and propagate any exceptions
            syncCtx.RunOnCurrentThread();
            t.GetAwaiter().GetResult();
        }
        finally { SynchronizationContext.SetSynchronizationContext(prevCtx); }
    }

    /// <summary>Provides a SynchronizationContext that's single-threaded.</summary>
    private sealed class SingleThreadSynchronizationContext : SynchronizationContext
    {
        /// <summary>The queue of work items.</summary>
        private readonly BlockingCollection<KeyValuePair<SendOrPostCallback, object>> m_queue = 
            new BlockingCollection<KeyValuePair<SendOrPostCallback, object>>();
        /// <summary>The processing thread.</summary>
        private readonly Thread m_thread = Thread.CurrentThread;

        /// <summary>Dispatches an asynchronous message to the synchronization context.</summary>
        /// <param name="d">The System.Threading.SendOrPostCallback delegate to call.</param>
        /// <param name="state">The object passed to the delegate.</param>
        public override void Post(SendOrPostCallback d, object state)
        {
            if (d == null) throw new ArgumentNullException("d");
            m_queue.Add(new KeyValuePair<SendOrPostCallback, object>(d, state));
        }

        /// <summary>Not supported.</summary>
        public override void Send(SendOrPostCallback d, object state)
        {
            throw new NotSupportedException("Synchronously sending is not supported.");
        }

        /// <summary>Runs an loop to process all queued work items.</summary>
        public void RunOnCurrentThread()
        {
            foreach (var workItem in m_queue.GetConsumingEnumerable())
                workItem.Key(workItem.Value);
        }

        /// <summary>Notifies the context that no more work will arrive.</summary>
        public void Complete() { m_queue.CompleteAdding(); }
    }
}

So you need to run AsyncPump on your main thread and give the SingleThreadSynchronizationContext to your worker thread so it can send work to your main thread.

void Main()
{
    AsyncPump.Run(async delegate
    {
       var syncContext = SynchronizationContext.Current;

       Console.WriteLine("Main thread, thradId:{0}", Thread.CurrentThread.ManagedThreadId); 

      await Task.Run(() =>
      {
        Console.WriteLine("Background thread, thradId:{0}", Thread.CurrentThread.ManagedThreadId);  

        syncContext.Post(new SendOrPostCallback((state) =>
        {
            Console.WriteLine("Running on main thread again, thradId:{0}", Thread.CurrentThread.ManagedThreadId);
        }), null);
     });

       Console.ReadLine();
    });;

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM