搜索一个 Task.Yield 等价于线程池

Searching for a Task.Yield equivalent that yields to the thread pool

Task.Yield 方法 “创建一个可等待的任务,该任务在等待时异步返回到当前上下文。” 我正在寻找类似的东西,它应该保证任何随后的代码将在 ThreadPool 线程上 运行。我知道我可以通过将以下所有代码包含在 Task.Run 中来实现这一点,但我正在寻找不创建内部范围的内联解决方案。

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 与空委托一起使用,并将等待配置为 not 捕获同步上下文:

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
}

这看起来像是一行晦涩难懂的代码,我不确定它是否充分表达了存在的意图。我也不确定它是否提供了我想要的保证。还有其他解决办法吗?

顺便说一句,这个问题的灵感来自 Marc Gravell 的 to a related .


更新: 我应该给出更具体的原因,说明为什么使用标准 await Task.Run(() => 在我的情况下并不理想。我有一些代码应该在 ThreadPool 上 运行 或不应该,这取决于某些条件。所以 Task.Yield 等价物可以让我这样做:

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 做同样的事情。

Raymond Chen 在他的博客 The Old New Thing in the post C++/WinRT envy: Bringing thread switching tasks to C# (WPF and WinForms edition).

中发布了此事

转载于此,以防源失效:

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

这让您可以:

await ThreadSwitcher.ResumeBackgroundAsync();

我对在共享代码库中实际执行此操作持谨慎态度:它不是特别地道,而 Task.Run 非常清楚。