在当前线程上执行任务

Tor*_*lin 22 c# task-parallel-library

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

也就是说,通过例如传递一些参数StartNew()来制作这段代码是否可能:

Task.Factory.StartNew(() => ThisShouldBeExecutedSynchronously());
Run Code Online (Sandbox Code Playgroud)

表现得像这样:

ThisShouldBeExecutedSynchronously();
Run Code Online (Sandbox Code Playgroud)

背景:

我有一个名为的界面IThreads:

public interface IThreads
{
    Task<TRet> StartNew<TRet>(Func<TRet> func);
}
Run Code Online (Sandbox Code Playgroud)

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

public class Threads : IThreads
{
    public Task<TRet> StartNew<TRet>(Func<TRet> func)
    {
        return Task.Factory.StartNew(func);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

public class NoThreading : IThreads
{
    public Task<TRet> StartNew<TRet>(Func<TRet> func)
    {
        // What do I write here?
    }
}
Run Code Online (Sandbox Code Playgroud)

我可以让NoThreading版本调用func(),但我想返回一个Task<TRet>我可以执行操作的实例,例如ContinueWith().

dca*_*tro 11

你可以简单地返回func()包装的结果Task.

public class NoThreading : IThreads
{
    public Task<TRet> StartNew<TRet>(Func<TRet> func)
    {
        return Task.FromResult(func());
    }
}
Run Code Online (Sandbox Code Playgroud)

现在您可以将"继续"任务附加到此.


Ond*_*cek 6

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

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

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

更多关于MSDN.

此外,如果你正在使用async/await那里已经有一个类似的问题.

  • 不幸的是,`RunSynchronously`并不总是同步运行任务.它有[类似于`ExecuteSynchronously`的极端情况](http://blogs.msdn.com/b/pfxteam/archive/2012/02/07/10265067.aspx). (5认同)

Ere*_*mez 6

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

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

var tcs = new TaskCompletionSource<TRet>();
tcs.SetResult(func());
return tcs.Task;
Run Code Online (Sandbox Code Playgroud)

返回故障任务:

var tcs = new TaskCompletionSource<TRet>();
tcs.SetException(new InvalidOperationException());
return tcs.Task;
Run Code Online (Sandbox Code Playgroud)

返回已取消的任务:

var tcs = new TaskCompletionSource<TRet>();
tcs.SetCanceled();
return tcs.Task;
Run Code Online (Sandbox Code Playgroud)


Tor*_*lin 5

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

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<TRet> StartNew<TRet>(Func<TRet> func)
    {
        var task = Task.Factory.StartNew(func, CancellationToken.None, TaskCreationOptions.None, _executeScheduler);
        return new TaskContinuation<TRet>(task, _continueScheduler);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

public class TaskContinuation<TRet>
{
    private readonly Task<TRet> _task;
    private readonly TaskScheduler _scheduler;

    public TaskContinuation(Task<TRet> task, TaskScheduler scheduler)
    {
        _task = task;
        _scheduler = scheduler;
    }

    public void ContinueWith(Action<Task<TRet>> func)
    {
        _task.ContinueWith(func, _scheduler);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

public class CurrentThreadScheduler : TaskScheduler
{
    private readonly Dispatcher _dispatcher;

    public CurrentThreadScheduler()
    {
        _dispatcher = Dispatcher.CurrentDispatcher;
    }

    protected override void QueueTask(Task task)
    {
        _dispatcher.BeginInvoke(new Func<bool>(() => TryExecuteTask(task)));
    }

    protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
    {
        return true;
    }

    protected override IEnumerable<Task> GetScheduledTasks()
    {
        return Enumerable.Empty<Task>();
    }
}
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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

DispatcherHelper.DoEvents();
Run Code Online (Sandbox Code Playgroud)

可以在这里DispatcherHelper找到。