C# execute code on other thread

后端 未结 2 1221
清歌不尽
清歌不尽 2021-01-23 04:11

I have some trouble with threading in my application. I have a multi-threaded client/server application. I\'m also using C# MonoDevelop for Unity3d. Not sure if it makes any dif

2条回答
  •  生来不讨喜
    2021-01-23 04:22

    .NET already has a concept of a SynchronizationContext, most often used for UI apps where thread affinity is required to invoke operations on UI controls (e.g. in WPF or WinForms). However, even outside a UI app, you can reuse these concepts for a general purpose thread-affinitized work queue.

    This sample shows how to use the WPF DispatcherSynchronizationContext (from WindowsBase.dll) in a simple console application, together with the .NET 4.0 task classes (TaskScheduler / Task) to invoke actions originating on child threads back on the main program thread.

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows.Threading;
    
    internal sealed class Program
    {
        private static void Main(string[] args)
        {
            int threadCount = 2;
            using (ThreadData data = new ThreadData(threadCount))
            {
                Thread[] threads = new Thread[threadCount];
                for (int i = 0; i < threadCount; ++i)
                {
                    threads[i] = new Thread(DoOperations);
                }
    
                foreach (Thread thread in threads)
                {
                    thread.Start(data);
                }
    
                Console.WriteLine("Starting...");
    
                // Start and wait here while all work is dispatched.
                data.RunDispatcher();
            }
    
            // Dispatcher has exited.
            Console.WriteLine("Shutdown.");
        }
    
        private static void DoOperations(object objData)
        {
            ThreadData data = (ThreadData)objData;
            try
            {
                // Start scheduling operations from child thread.
                for (int i = 0; i < 5; ++i)
                {
                    int t = Thread.CurrentThread.ManagedThreadId;
                    int n = i;
                    data.ExecuteTask(() => SayHello(t, n));
                }
            }
            finally
            {
                // Child thread is done.
                data.OnThreadCompleted();
            }
        }
    
        private static void SayHello(int requestingThreadId, int operationNumber)
        {
            Console.WriteLine(
                "Saying hello from thread {0} ({1}) on thread {2}.",
                requestingThreadId,
                operationNumber,
                Thread.CurrentThread.ManagedThreadId);
        }
    
        private sealed class ThreadData : IDisposable
        {
            private readonly Dispatcher dispatcher;
            private readonly TaskScheduler scheduler;
            private readonly TaskFactory factory;
            private readonly CountdownEvent countdownEvent;
    
            // In this example, we initialize the countdown event with the total number
            // of child threads so that we know when all threads are finished scheduling
            // work.
            public ThreadData(int threadCount)
            {
                this.dispatcher = Dispatcher.CurrentDispatcher;
                SynchronizationContext context = 
                    new DispatcherSynchronizationContext(this.dispatcher);
                SynchronizationContext.SetSynchronizationContext(context);
                this.scheduler = TaskScheduler.FromCurrentSynchronizationContext();
                this.factory = new TaskFactory(this.scheduler);
                this.countdownEvent = new CountdownEvent(threadCount);
            }
    
            // This method should be called by a child thread when it wants to invoke
            // an operation back on the main dispatcher thread.  This will block until
            // the method is done executing.
            public void ExecuteTask(Action action)
            {
                Task task = this.factory.StartNew(action);
                task.Wait();
            }
    
            // This method should be called by threads when they are done
            // scheduling work.
            public void OnThreadCompleted()
            {
                bool allThreadsFinished = this.countdownEvent.Signal();
                if (allThreadsFinished)
                {
                    this.dispatcher.InvokeShutdown();
                }
            }
    
            // This method should be called by the main thread so that it will begin
            // processing the work scheduled by child threads. It will return when
            // the dispatcher is shutdown.
            public void RunDispatcher()
            {
                Dispatcher.Run();
            }
    
            public void Dispose()
            {
                this.Dispose(true);
                GC.SuppressFinalize(this);
            }
    
            // Dispose all IDisposable resources.
            private void Dispose(bool disposing)
            {
                if (disposing)
                {
                    this.countdownEvent.Dispose();
                }
            }
        }
    }
    

    Sample output:

    Starting...
    Saying hello from thread 3 (0) on thread 1.
    Saying hello from thread 4 (0) on thread 1.
    Saying hello from thread 3 (1) on thread 1.
    Saying hello from thread 4 (1) on thread 1.
    Saying hello from thread 3 (2) on thread 1.
    Saying hello from thread 4 (2) on thread 1.
    Saying hello from thread 3 (3) on thread 1.
    Saying hello from thread 4 (3) on thread 1.
    Saying hello from thread 3 (4) on thread 1.
    Saying hello from thread 4 (4) on thread 1.
    Shutdown.
    

提交回复
热议问题