2

假设我创建了一个包含这样的方法库:如何将同步上下文/任务调度程序替换为TaskCompletionSource.Task中的另一个ConfigureAwait(false)?

Task MyLibraryMethodAsync() 
{ 
    var taskCompletionSource = new TaskCompletionSource<object>(); 

    Action myWorkItem = 
     () => 
     { 
      // Simulate some work. 
      // Actual work items depend on input params. 
      Thread.Sleep(TimeSpan.FromSeconds(1)); 

      taskCompletionSource.SetResult(null); 
     }; 

    // The next two lines is simplification for demonstration. 
    // I do not have access to the workerThread - it is created 
    // and managed for me by another lib. 
    // All I can do - is to post some short work items to it. 
    var workerThread = new Thread(new ThreadStart(myWorkItem)); 
    workerThread.Start(); 

    return taskCompletionSource.Task; 
} 

我的lib中的任何用户都可以拨打MyLibraryMethodAsync这样

await MyLibraryMethodAsync().ConfigureAwait(false); 
VeryLongRunningMethod(); 
void VeryLongRunningMethod() 
{ 
    Thread.Sleep(TimeSpan.FromHours(1)); 
} 

在这里,问题就来了 - VeryLongRunningMethod将内被执行taskCompletionSource.SetResult(null)调用,因此它会阻止workerThread很长一段时间,这是不希望的行为,因为workerThread是为了运行小porti代码(工作项目)。

如何替代上下文/调度到线程池返回的任务,因此await x.ConfigureAwait(false)继续对线程池内,但不是在workerThread

目前的解决方案,我发现是

Task MyLibraryMethodAsync() 
{ 
    // ... 

    return taskCompletionSource.Task 
     .ContinueWith(x => x.Result, TaskScheduler.Default); 
} 

不过,我不喜欢它由于开销它创建。 可能更优雅的解决方案存在?

回答

3

从.NET 4.6开始,TaskCreationOptions中有一个名为RunContinuationsAsynchronously的选项,它可以完全按照您的需要进行操作,它可以确保所有延续都是异步运行的,而不是在设置结果时同步运行。 TaskCompletionSource在其构造函数中有一个可选的TaskCreationOption参数供您提供该选项。

如果您使用.NET的早期版本,则需要做一个效率较低的黑客攻击,如添加另一个继续项,或者在线程池线程中显式设置结果,而不是通过回调行动。

0

不知道如果我理解正确,但你可以明确地创建一个后台任务,以避免阻塞:

await MyLibraryMethodAsync().ConfigureAwait(false); 
await Task.Run(() => VeryLongRunningMethod()); 

你甚至可以离开了ConfigureAwait则:

await MyLibraryMethodAsync() 
await Task.Run(() => VeryLongRunningMethod()); 

编辑:基于关于你的评论:如果你作为图书馆的作者想要阻止线程阻塞,你可以使用:

Task.Run(() => taskCompletionSource.SetResult(null)); 
+0

从用户的角度来看,你是对的。但我是lib开发人员,我需要确保lib用户不会阻止工作线程。 –

+2

请参阅编辑 - 使用Task.Run(()=> taskCompletionSource.SetResult(null));设置结果? – Matthias

+0

这给出了与我目前的解决方案相同的开销。无论如何,塞维解决了我的问题。 –

相关问题