【问题标题】:Task vs Continuation scheduling任务与继续调度
【发布时间】:2016-04-21 06:44:06
【问题描述】:

假设我有许多 CPU 密集型任务被转储到默认调度程序上(所有任务同时运行,例如通过 Task.RunTask.Factory.StartNew 使用默认调度程序)。每个任务都有一个延续。调度程序将启动一些任务并将它们置于运行状态。当这些任务完成时,调度程序会在其队列中混合原始任务和后续任务进行调度,并且必须在它们之间进行选择。它如何优先考虑延续与运行更多的组织任务。具体来说,如果一个任务已经完成并且它有一个延续,那么该延续在调度程序中的优先级是否高于已经排队的其他任务。

例如,假设调度程序运行许多 (T1...Tn) 排队任务中的​​ 2 个(T1 和 T2)。当其中一个完成时,调度程序是否必须运行延续 C1 或者它可能决定启动 T3?无论如何它会选择哪个是确定性的吗?调度器是否可能会选择运行更多的任务,因此在 T1 结束和其延续 C1 开始之间可能会有相当大的延迟?

更新:我已经运行了一些示例代码并添加了一个答案 - 但我仍然想知道这种观察到的行为是否得到保证?

【问题讨论】:

  • 哪个调度器?你知道你可以实现你自己的吗?你是说默认调度器?
  • @TomTom 问题已澄清。
  • 依靠调度程序对任务的任何排序似乎是愚蠢的高度,除了由您通过延续强制执行并订购awaits 的排序。我很想明确地说不,这不能保证,因为它会给实施带来过度的负担,一直被束缚于此,但由于我不是微软员工,我这样做不舒服。不过,总的来说:换句话说,如果你不同步事物,那么事物就不会同步,除非是偶然的。

标签: c# multithreading task-parallel-library task taskscheduler


【解决方案1】:

所以我用一个样本试了一下:

var continuations = new List<Task>();
for (int i = 0; i < 20; i++) {
    int counter = i;
    var continuation = Task.Run(() => {
        Console.WriteLine($"T{counter}: Start.");
        Thread.Sleep(500);
        Console.WriteLine($"T{counter}: Complete.");
    }).ContinueWith(t => {
        Console.WriteLine($"C{counter}: Start.");
        Thread.Sleep(50);
        Console.WriteLine($"C{counter}: Complete.");
    });
    continuations.Add(continuation);
}
Task.WaitAll(continuations.ToArray());

哪个输出以下 - 暗示调度程序确实优先于启动新任务的延续(但这是保证吗?)

T1: Start.
T2: Start.
T3: Start.
T0: Start.
T1: Complete.
T2: Complete.
T3: Complete.
C1: Start.
C3: Start.
T0: Complete.
C0: Start.
C2: Start.
C1: Complete.
T4: Start.
C3: Complete.
T5: Start.
C0: Complete.
T6: Start.
C2: Complete.
T7: Start.
T4: Complete.
C4: Start.
T5: Complete.
C5: Start.
T6: Complete.
C6: Start.
T7: Complete.
C7: Start.
C4: Complete.
C5: Complete.
T9: Start.
T8: Start.
C7: Complete.
T10: Start.
C6: Complete.
T11: Start.
T8: Complete.
C8: Start.
T9: Complete.
C9: Start.
T11: Complete.
C11: Start.
T10: Complete.
C10: Start.
C9: Complete.
C8: Complete.
T13: Start.
T12: Start.
C11: Complete.
T14: Start.
C10: Complete.

一种变体。这里在启动 Task 和注册 Continuation 之间在调用线程上有额外的执行。

var continuations = new List<Task>();
for (int i = 0; i < 20; i++) {
    int counter = i;
    var task = Task.Run(() => {
        Console.WriteLine($"T{counter}: Start.");
        Thread.Sleep(500);
        Console.WriteLine($"T{counter}: Complete.");
    });
    Thread.Sleep(100); // Do some stuff before registering continuation.
    var continuation = task.ContinueWith(t => {
    Console.WriteLine($"C{counter}: Start.");
        Thread.Sleep(150);
        Console.WriteLine($"C{counter}: Complete.");
    });
    continuations.Add(continuation);
}
Task.WaitAll(continuations.ToArray());

结果与上述情况相同,即任务 N 的继续被赋予其他排队任务的优先级。

T0: Start.
T1: Start.
T2: Start.
T3: Start.
T0: Complete.
C0: Start.
T1: Complete.
C1: Start.
C0: Complete.
T4: Start.
T2: Complete.
C2: Start.
C1: Complete.
T5: Start.
T3: Complete.
C3: Start.
C2: Complete.
T6: Start.
C3: Complete.
T7: Start.
T4: Complete.
C4: Start.
T5: Complete.
C5: Start.
C4: Complete.
T8: Start.
T6: Complete.
C6: Start.
C5: Complete.
T9: Start.
T7: Complete.
C7: Start.
C6: Complete.
T10: Start.
C7: Complete.
T11: Start.
T8: Complete.
C8: Start.
T9: Complete.
C9: Start.
C8: Complete.
T12: Start.
T10: Complete.
C10: Start.
C9: Complete.
T13: Start.
T11: Complete.
C11: Start.
T14: Start.
C10: Complete.
T15: Start.
C11: Complete.
T16: Start.
T12: Complete.
C12: Start.
T13: Complete.
C13: Start.
T14: Complete.
C14: Start.
C12: Complete.
T17: Start.
T15: Complete.
C15: Start.......

另一种变化。在注册延续之前,所有任务都首先在 scheuler 上排队(并且可以安排运行)。

const int taskCount = 20;
var tasks = new List<Task>();
for (int i = 0; i < taskCount; i++) {
    int counter = i;
    var task = Task.Run(() => {
        Console.WriteLine($"T{counter}: Start.");
        Thread.Sleep(500);
        Console.WriteLine($"T{counter}: Complete.");
    });
    tasks.Add(task);
}
Thread.Sleep(400);
var continuations = new List<Task>();
for (int i = 0; i < taskCount; i++) {
    int counter = i;
    var continuation = tasks[i].ContinueWith(t => {
        Console.WriteLine($"C{counter}: Start.");
        Thread.Sleep(150);
        Console.WriteLine($"C{counter}: Complete.");
    });
    continuations.Add(continuation);
}
Task.WaitAll(continuations.ToArray());

结果和以前一样 - 继续优先。

T0: Start.
T2: Start.
T3: Start.
T1: Start.
T1: Complete.
T3: Complete.
T0: Complete.
T2: Complete.
C2: Start.
C0: Start.
C3: Start.
C1: Start.
C2: Complete.
C1: Complete.
C0: Complete.
C3: Complete.
T7: Start.
T4: Start.
T6: Start.
T5: Start.
T6: Complete.
T5: Complete.
T7: Complete.
T4: Complete.
C6: Start.
C5: Start.
C7: Start.
C4: Start.
C7: Complete.
T8: Start.
C5: Complete.
T9: Start.
C6: Complete.
T10: Start.
C4: Complete.
T11: Start.
T8: Complete.
C8: Start.
T10: Complete.
C10: Start.
T9: Complete.
C9: Start.
T11: Complete.
C11: Start.
C8: Complete.
T12: Start.
C9: Complete.
T13: Start.
C10: Complete.
T14: Start.
C11: Complete.
T15: Start.
T14: Complete.
T12: Complete.
C12: Start.
T13: Complete.
C13: Start.
C14: Start.
T15: Complete.
C15: Start.
T16: Start.
C12: Complete.
C13: Complete.
T18: Start.
T17: Start.
C14: Complete.
T19: Start.
C15: Complete.
T16: Complete.
C16: Start.
T18: Complete.
C18: Start.
T17: Complete.
T19: Complete.
C19: Start.
C16: Complete.
C17: Start.
C18: Complete.
C19: Complete.
C17: Complete.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-02-22
    • 2015-04-10
    • 2014-02-04
    • 1970-01-01
    • 2013-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多