搜索产生线程池的Task.Yield等效项
Task.Yield
方法"创建一个可等待的任务,该任务在等待时异步地返回当前上下文." 我正在寻找类似的东西,这些东西应保证后面的任何代码都会在 ThreadPool
线程上运行.我知道可以通过将以下所有代码包含在 Task.Run
中来实现此目的,但是我正在寻找不会创建内部作用域的内联解决方案.
The Task.Yield
method "creates an awaitable task that asynchronously yields back to the current context when awaited." I am searching for something similar that should guarantee that any code that follows will run on a ThreadPool
thread. I know that I could achieve this be enclosing all the following code in a Task.Run
, but I am searching for an inline solution that doesn't create an inner scope.
private async void Button1_Click(object sender, EventArgs e)
{
await Task.Yield(); // Doesn't do what I want
// Code that should run on the ThreadPool
}
private async void Button1_Click(object sender, EventArgs e)
{
// Does what I want, but I am not happy with the added indentation and scope
await Task.Run(() =>
{
// Code that should run on the ThreadPool
});
}
我能想到的最好的方法是将 Task.Run
与空的委托一起使用,并将等待配置为不捕获同步上下文:
The best I can think of is to use the Task.Run
with an empty delegate, and configure the awaiting to not capture the synchronization context:
private async void Button1_Click(object sender, EventArgs e)
{
// Probably does what I want, but it looks ugly
await Task.Run(() => { }).ConfigureAwait(continueOnCapturedContext: false);
// Code that should run on the ThreadPool
}
这看起来像是一句晦涩难懂的代码,而且我不确定这是否足以表达在那里的意图.我也不确定它是否提供我想要的保证.还有其他解决方案吗?
This looks like an obscure line of code, and I am not sure that expresses well enough the intent of being there. I am also not sure if it provides the guarantees I want. Is there any other solution?
顺便说一句,这个问题的灵感来自马克·格雷夫(Marc Gravell)的 answer到相关的问题.
Btw this question is inspired by a Marc Gravell's answer to a related question.
更新:对于我为什么不适合使用标准 await Task.Run(()=>
)的原因,我应该给出一个更具体的原因.某些条件下,某些代码应该在 ThreadPool
上运行或不运行,因此等效于 Task.Yield
可以使我做到这一点:
Update: I should give a more specific reason about why using the standard await Task.Run(() =>
is not ideal in my case. I have some code that should run on the ThreadPool
or not, depending on some condition. So a Task.Yield
equivalent would allow me to do this:
private bool _executeOnThreadPool;
private async void Button1_Click(object sender, EventArgs e)
{
if (_executeOnThreadPool) await SwitchToTheThreadPool();
// Code that should run on the ThreadPool or the UI thread
}
如果没有代码重复或没有添加lambda和间接调用,我无法对 Task.Run
做同样的事情.
I can't do the same thing with Task.Run
without code duplication, or without adding lambdas and indirection.
Raymond Chen在他的博客帖子 C ++/WinRT令人羡慕:带来线程切换任务到C#(WPF和WinForms版).
Raymond Chen posted about this in his blog The Old New Thing in the post C++/WinRT envy: Bringing thread switching tasks to C# (WPF and WinForms edition).
如果源掉了,请在此处复制:
Reproduced here in case the source goes down:
using System;
using System.Runtime.CompilerServices;
using System.Threading; // For ThreadPool
using System.Windows.Forms; // For Windows Forms
using System.Windows.Threading; // For WPF
// For WPF
struct DispatcherThreadSwitcher : INotifyCompletion
{
internal DispatcherThreadSwitcher(Dispatcher dispatcher) =>
this.dispatcher = dispatcher;
public DispatcherThreadSwitcher GetAwaiter() => this;
public bool IsCompleted => dispatcher.CheckAccess();
public void GetResult() { }
public void OnCompleted(Action continuation) =>
dispatcher.BeginInvoke(continuation);
Dispatcher dispatcher;
}
// For Windows Forms
struct ControlThreadSwitcher : INotifyCompletion
{
internal ControlThreadSwitcher(Control control) =>
this.control = control;
public ControlThreadSwitcher GetAwaiter() => this;
public bool IsCompleted => !control.InvokeRequired;
public void GetResult() { }
public void OnCompleted(Action continuation) =>
control.BeginInvoke(continuation);
Control control;
}
// For both WPF and Windows Forms
struct ThreadPoolThreadSwitcher : INotifyCompletion
{
public ThreadPoolThreadSwitcher GetAwaiter() => this;
public bool IsCompleted =>
SynchronizationContext.Current == null;
public void GetResult() { }
public void OnCompleted(Action continuation) =>
ThreadPool.QueueUserWorkItem(_ => continuation());
}
class ThreadSwitcher
{
// For WPF
static public DispatcherThreadSwitcher ResumeForegroundAsync(
Dispatcher dispatcher) =>
new DispatcherThreadSwitcher(dispatcher);
// For Windows Forms
static public ControlThreadSwitcher ResumeForegroundAsync(
Control control) =>
new ControlThreadSwitcher(control);
// For both WPF and Windows Forms
static public ThreadPoolThreadSwitcher ResumeBackgroundAsync() =>
new ThreadPoolThreadSwitcher();
}
这可让您执行以下操作:
This lets you do:
await ThreadSwitcher.ResumeBackgroundAsync();
但是,我会在共享代码库中实际执行此操作时要小心:这不是特别习惯,而 Task.Run
非常清楚.