简体   繁体   English

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

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

Does TaskScheduler.Default not always guarantee the task will be executed on a pool thread? TaskScheduler.Default 总是保证任务将在池线程上执行吗?

While fixing a bug, I found at least one case when it doesn't. 在修复错误时,我发现至少有一个案例没有。 It can be reproduced like this (a contrived example made from real code): 它可以像这样再现(一个由真实代码制作的人为例子):

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

Are there any other cases? 还有其他案件吗?

Also, is there an elegant way to make sure the ContinueWith action is executed synchronously, if the antecedent task has completed on a pool thread, or gets queued to thread pool otherwise (I know I can use QueueUserWorkItem in ContinueWith action, but I don't like it). 此外,是否有一种优雅的方法可以确保ContinueWith操作是同步执行的,如果先前的任务已在池线程上完成,或者排队等待线程池(我知道我可以在ContinueWith操作中使用QueueUserWorkItem ,但我不知道)喜欢它)。

EDITED, I guess I can implement my own TaskScheduler and check if I am already on a thread pool thread inside TryExecuteTaskInline , to control this. 编辑,我想我可以实现自己的TaskScheduler并检查我是否已经在TryExecuteTaskInline内的线程池线程中来控制它。

I think the reason this happens is because you use the TaskContinuationOptions.ExecuteSynchronously . 我认为发生这种情况的原因是因为您使用TaskContinuationOptions.ExecuteSynchronously From the doco: 来自doco:

ExecuteSynchronously Specifies that the continuation task should be executed synchronously. ExecuteSynchronously指定应同步执行continuation任务。 With this option specified, the continuation will be run on the same thread that causes the antecedent task to transition into its final state. 如果指定了此选项,则将在导致先行任务转换到其最终状态的同一线程上运行continuation。 If the antecedent is already complete when the continuation is created, the continuation will run on the thread creating the continuation. 如果在创建延续时前提已经完成,则继续将在创建延续的线程上运行。 Only very short-running continuations should be executed synchronously. 只有非常短暂的延续才能同步执行。

If the antecedent task completes on a thread other than a thread pool thread then the continuation will run on that thread also. 如果先前任务在线程池线程以外的线程上完成,那么继续也将在该线程上运行。 So I guess in that case no scheduling occurs. 所以我想在那种情况下不会发生任何调度。 Another case might be when the continuation task is already complete, which will also run synchronously. 另一种情况可能是延续任务已经完成,也将同步运行。

Update 更新

To achieve what you are asking in your second part of the question I think you'll need a custom awaiter. 为了达到你在问题的第二部分所要求的内容,我认为你需要一个定制的等待者。 See this articl e for more details but something like this might work for you: 有关更多详细信息,请参阅内容,但这样的内容可能对您有用

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

Then you use it like this: 然后你像这样使用它:

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