Execute task on current thread

后端 未结 5 1024
灰色年华
灰色年华 2021-02-05 05:35

Is it possible to force a task to execute synchronously, on the current thread?

That is, is it possible, by e.g. passing some parameter to StartNew(), to ma

5条回答
  •  别跟我提以往
    2021-02-05 06:24

    OP here. This is my final solution (which actually solves a lot more than I asked about).

    I use the same implementation for Threads in both test and production, but pass in different TaskSchedulers:

    public class Threads
    {
        private readonly TaskScheduler _executeScheduler;
        private readonly TaskScheduler _continueScheduler;
    
        public Threads(TaskScheduler executeScheduler, TaskScheduler continueScheduler)
        {
            _executeScheduler = executeScheduler;
            _continueScheduler = continueScheduler;
        }
    
        public TaskContinuation StartNew(Func func)
        {
            var task = Task.Factory.StartNew(func, CancellationToken.None, TaskCreationOptions.None, _executeScheduler);
            return new TaskContinuation(task, _continueScheduler);
        }
    }
    

    I wrap the Task in a TaskContinuation class in order to be able to specify TaskScheduler for the ContinueWith() call.

    public class TaskContinuation
    {
        private readonly Task _task;
        private readonly TaskScheduler _scheduler;
    
        public TaskContinuation(Task task, TaskScheduler scheduler)
        {
            _task = task;
            _scheduler = scheduler;
        }
    
        public void ContinueWith(Action> func)
        {
            _task.ContinueWith(func, _scheduler);
        }
    }
    

    I create my custom TaskScheduler that dispatches the action on the thread that scheduler was created on:

    public class CurrentThreadScheduler : TaskScheduler
    {
        private readonly Dispatcher _dispatcher;
    
        public CurrentThreadScheduler()
        {
            _dispatcher = Dispatcher.CurrentDispatcher;
        }
    
        protected override void QueueTask(Task task)
        {
            _dispatcher.BeginInvoke(new Func(() => TryExecuteTask(task)));
        }
    
        protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
        {
            return true;
        }
    
        protected override IEnumerable GetScheduledTasks()
        {
            return Enumerable.Empty();
        }
    }
    

    Now I can specify the behaviour by passing in different TaskSchedulers to the Threads constructor.

    new Threads(TaskScheduler.Default, TaskScheduler.FromCurrentSynchronizationContext()); // Production
    new Threads(TaskScheduler.Default, new CurrentThreadScheduler()); // Let the tests use background threads
    new Threads(new CurrentThreadScheduler(), new CurrentThreadScheduler()); // No threads, all synchronous
    

    Finally, since the event loop doesn't run automatically in my unit test, I have to execute it manually. Whenever I need to wait for a background operation to complete I execute the following (from the main thread):

    DispatcherHelper.DoEvents();
    

    The DispatcherHelper can be found here.

提交回复
热议问题