繁体   English   中英

TaskScheduler.Default不总是保证任务将在池线程上执行吗?

[英]Does TaskScheduler.Default not always guarantee the task will be executed on a pool thread?

TaskScheduler.Default 总是保证任务将在池线程上执行吗?

在修复错误时,我发现至少有一个案例没有。 它可以像这样再现(一个由真实代码制作的人为例子):

var tcs = new TaskCompletionSource<bool>();
var sc = SynchronizationContext.Current;
sc.Post(_ => tcs.SetResult(true), null);
await tcs.Task.ContinueWith(_ =>
    {
        // breaks here
        Debug.Assert(Thread.CurrentThread.IsThreadPoolThread);
    }, 
    CancellationToken.None, 
    TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);

还有其他案件吗?

此外,是否有一种优雅的方法可以确保ContinueWith操作是同步执行的,如果先前的任务已在池线程上完成,或者排队等待线程池(我知道我可以在ContinueWith操作中使用QueueUserWorkItem ,但我不知道)喜欢它)。

编辑,我想我可以实现自己的TaskScheduler并检查我是否已经在TryExecuteTaskInline内的线程池线程中来控制它。

我认为发生这种情况的原因是因为您使用TaskContinuationOptions.ExecuteSynchronously 来自doco:

ExecuteSynchronously指定应同步执行continuation任务。 如果指定了此选项,则将在导致先行任务转换到其最终状态的同一线程上运行continuation。 如果在创建延续时前提已经完成,则继续将在创建延续的线程上运行。 只有非常短暂的延续才能同步执行。

如果先前任务在线程池线程以外的线程上完成,那么继续也将在该线程上运行。 所以我想在那种情况下不会发生任何调度。 另一种情况可能是延续任务已经完成,也将同步运行。

更新

为了达到你在问题的第二部分所要求的内容,我认为你需要一个定制的等待者。 有关更多详细信息,请参阅内容,但这样的内容可能对您有用

public static class Extensions
{
    public static ThreadPoolTaskAwaiter WithThreadPool(this Task task)
    {
        return new ThreadPoolTaskAwaiter(task);
    }

    public class ThreadPoolTaskAwaiter : INotifyCompletion
    {
        private readonly TaskAwaiter m_awaiter;

        public ThreadPoolTaskAwaiter(Task task)
        {
            if (task == null) throw new ArgumentNullException("task");
            m_awaiter = task.GetAwaiter();
        }

        public ThreadPoolTaskAwaiter GetAwaiter() { return this; }

        public bool IsCompleted { get { return m_awaiter.IsCompleted; } }

        public void OnCompleted(Action continuation)
        {
            if (Thread.CurrentThread.IsThreadPoolThread)
            {
                continuation();
            }
            else
            {
                Task.Run(continuation);
            }                
        }

        public void GetResult()
        {
            m_awaiter.GetResult();
        }
    }
}

然后你像这样使用它:

public static async Task Execute()
{          
    await Task.Delay(500).WithThreadPool();

    // does not break here
    if (!Thread.CurrentThread.IsThreadPoolThread)
    {
        Debugger.Break();
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM