搜索产生线程池的Task.Yield等效项 [英] Searching for a Task.Yield equivalent that yields to the thread pool
问题描述
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
非常清楚.
这篇关于搜索产生线程池的Task.Yield等效项的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!