在当前线程上执行任务

是否可以在当前线程上强制执行任务同步执行?

也就是说,通过例如将一些参数传递给StartNew() ,可以生成此代码:

 Task.Factory.StartNew(() => ThisShouldBeExecutedSynchronously()); 

表现得像这样:

 ThisShouldBeExecutedSynchronously(); 

背景:

我有一个名为IThreads的界面:

 public interface IThreads { Task StartNew(Func func); } 

我想有两个这样的实现,一个使用线程的普通:

 public class Threads : IThreads { public Task StartNew(Func func) { return Task.Factory.StartNew(func); } } 

而且一个不使用线程(在某些测试场景中使用):

 public class NoThreading : IThreads { public Task StartNew(Func func) { // What do I write here? } } 

我可以让NoThreading版本只调用func() ,但我想返回一个Task的实例,我可以在其上执行ContinueWith()等操作。

任务计划程序决定是在新线程上还是在当前线程上运行任务。 有一个选项强制在新线程上运行它,但没有强制它在当前线程上运行。

但是有一个方法Task.RunSynchronously()

在当前TaskScheduler上同步运行任务。

更多关于MSDN 。

此外,如果您使用async/await ,则已经存在类似的问题 。

您只需返回包装在Taskfunc()的结果即可。

 public class NoThreading : IThreads { public Task StartNew(Func func) { return Task.FromResult(func()); } } 

现在您可以将“继续”任务附加到此。

由于您提到了测试,您可能更喜欢使用TaskCompletionSource因为它还允许您设置exception或将任务设置为已取消(在.Net 4和4.5中工作):

返回已完成的任务,结果如下:

 var tcs = new TaskCompletionSource(); tcs.SetResult(func()); return tcs.Task; 

返回故障任务:

 var tcs = new TaskCompletionSource(); tcs.SetException(new InvalidOperationException()); return tcs.Task; 

返回已取消的任务:

 var tcs = new TaskCompletionSource(); tcs.SetCanceled(); return tcs.Task; 

OP在这里。 这是我的最终解决方案(实际上解决的问题比我提出的要多得多)。

我在测试和生产中使用相同的Threads实现,但传入不同的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); } } 

我将Task包装在TaskContinuation类中,以便能够为ContinueWith()调用指定TaskScheduler

 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); } } 

我创建了自定义TaskScheduler ,它在创建调度程序的线程上调度操作:

 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(); } } 

现在我可以通过将不同的TaskSchedulers传递给Threads构造函数来指定行为。

 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 

最后,由于事件循环不会在我的unit testing中自动运行,我必须手动执行它。 每当我需要等待后台操作完成时,我执行以下操作(从主线程):

 DispatcherHelper.DoEvents(); 

DispatcherHelper可以在这里找到。

是的,你可以使用自定义任务调度程序来做到这一点。

 internal class MyScheduler : TaskScheduler { protected override IEnumerable GetScheduledTasks() { return Enumerable.Empty(); } protected override void QueueTask(Task task) { base.TryExecuteTask(task); } protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) { base.TryExecuteTask(task); return true; } } static void Main(string[] args) { Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " Main"); Task.Factory.StartNew(() => ThisShouldBeExecutedSynchronously(), CancellationToken.None, TaskCreationOptions.None, new MyScheduler()); }