延续任务上的Task.WaitAll()仅延迟原始任务的执行?

乔安娜·德克斯(Joanna Derks)

背景:

我有一个控制台应用程序,该应用程序创建Tasks来处理来自数据库的数据(我们称它们为Level1任务)。每个任务都再次创建自己的任务,以处理分配给它的数据的每个部分(Level2任务)。

每个Level2任务都有一个与其关联的继续任务,以及WaitAll在继续执行之前用于继续任务的代码

我在.NET 4.0(否async/ await

问题:

但是,这产生了一个问题-事实证明,如果以此方式完成,则在计划所有可用的Level1任务之前,不会启动任何Level2任务。这绝不是最佳选择。

题:

更改代码以等待原始的Level2任务及其继续任务似乎已解决了该问题。但是,我不确定为什么会这样。

你有什么想法?

我唯一能想到的是-由于继续任务尚未开始,因此没有必要等待它完成。但是即使是这种情况,我也希望至少已经有一些Level2任务开始了。他们从来没有做过。

例:

我创建了一个示例控制台应用程序,演示了该行为:

  1. 按原样运行它,您会看到它首先调度了所有任务,然后才开始从Level2任务中写入实际的行。

  2. 但是请注释掉标记的代码块,并取消注释替换,所有代码均按预期工作。

你能告诉我为什么吗?

public class Program
{
    static void Main(string[] args)
    {
        for (var i = 0; i < 100; i++)
        {
            Task.Factory.StartNew(() => SomeMethod());
            //Thread.Sleep(1000);
        }

        Console.ReadLine();
    }

    private static void SomeMethod()
    {
        var numbers = new List<int>();

        for (var i = 0; i < 10; i++)
        {
            numbers.Add(i);
        }

        var tasks = new List<Task>();

        foreach (var number in numbers)
        {
            Console.WriteLine("Before start task");

            var numberSafe = number;

            /* Code to be replaced START */

            var nextTask = Task.Factory.StartNew(() =>
            {
                Console.WriteLine("Got number: {0}", numberSafe);
            })
                .ContinueWith(task =>
                {
                    Console.WriteLine("Continuation {0}", task.Id);
                });

            tasks.Add(nextTask);

            /* Code to be replaced END */

            /* Replacement START */

            //var originalTask = Task.Factory.StartNew(() =>
            //{
            //    Console.WriteLine("Got number: {0}", numberSafe);
            //});

            //var contTask = originalTask
            //    .ContinueWith(task =>
            //    {
            //        Console.WriteLine("Continuation {0}", task.Id);
            //    });

            //tasks.Add(originalTask);
            //tasks.Add(contTask);

            /* Replacement END */
        }

        Task.WaitAll(tasks.ToArray());
    }
}
YK1

我认为您正在看到这种Task Inlining行为。MSDN引用

在某些情况下,在等待任务时,可以在执行等待操作的线程上同步执行该任务。这样可以提高性能,因为它可以通过利用现有的可能会阻塞的线程来避免对其他线程的需求。为了防止由于重新进入而导致错误,仅当在相关线程的本地队列中找到等待目标时才发生任务内联。

您不需要100个任务即可看到此信息。我已将您的程序修改为具有4个1级任务(我具有四核CPU)。每个1级任务仅创建一个2级任务。

static void Main(string[] args)
{
    for (var i = 0; i < 4; i++)
    {
        int j = i;
        Task.Factory.StartNew(() => SomeMethod(j)); // j as level number
    }
}

在您的原始程序中,这nextTask是继续任务-所以我只是简化了方法。

private static void SomeMethod(int num)
{
    var numbers = new List<int>();

    // create only one level 2 task for representation purpose
    for (var i = 0; i < 1; i++)
    {
        numbers.Add(i);
    }

    var tasks = new List<Task>();

    foreach (var number in numbers)
    {
        Console.WriteLine("Before start task: {0} - thread {1}", num, 
                              Thread.CurrentThread.ManagedThreadId);

        var numberSafe = number;

        var originalTask = Task.Factory.StartNew(() =>
        {
            Console.WriteLine("Got number: {0} - thread {1}", num, 
                                    Thread.CurrentThread.ManagedThreadId);
        });

        var contTask = originalTask
            .ContinueWith(task =>
            {
                Console.WriteLine("Continuation {0} - thread {1}", num, 
                                    Thread.CurrentThread.ManagedThreadId);
            });

        tasks.Add(originalTask); // comment and un-comment this line to see change in behavior

        tasks.Add(contTask); // same as adding nextTask in your original prog.

    }

    Task.WaitAll(tasks.ToArray());
}

这是示例输出-注释tasks.Add(originalTask);-这是您的第一个块。

Before start task: 0 - thread 4
Before start task: 2 - thread 3
Before start task: 3 - thread 6
Before start task: 1 - thread 5
Got number: 0 - thread 7
Continuation 0 - thread 7
Got number: 1 - thread 7
Continuation 1 - thread 7
Got number: 3 - thread 7
Continuation 3 - thread 7
Got number: 2 - thread 4
Continuation 2 - thread 4

还有一些示例输出-保持tasks.Add(originalTask);哪个是您的第二个块

Before start task: 0 - thread 4
Before start task: 1 - thread 6
Before start task: 2 - thread 5
Got number: 0 - thread 4
Before start task: 3 - thread 3
Got number: 3 - thread 3
Got number: 1 - thread 6
Got number: 2 - thread 5
Continuation 0 - thread 7
Continuation 1 - thread 7
Continuation 3 - thread 7
Continuation 2 - thread 4

正如您在第二种情况下看到的那样,当您originalTask在启动它的同一线程上等待时task inlining它将使它在同一线程上运行-这就是为什么您Got Number..较早看到消息的原因

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

C#中的Task.WaitAll挂起了任务的执行

来自分类Dev

C#中的Task.WaitAll挂起了任务的执行

来自分类Dev

在for循环中启动任务,然后在所有任务上执行waitall

来自分类Dev

延迟然后执行任务

来自分类Dev

使用Task.WaitAll()处理等待的任务?

来自分类Dev

任务等待vs Task.WaitAll

来自分类Dev

任务等待vs Task.WaitAll

来自分类Dev

如何跨任务延续块序列化线程执行?

来自分类Dev

如何为已取消的任务添加延续

来自分类Dev

我应该使用常规任务还是延续任务?

来自分类Dev

Gradle 任务仅执行 uiTests

来自分类Dev

子任务与WaitAll

来自分类Dev

子任务与WaitAll

来自分类Dev

任务延迟

来自分类Dev

任务的延续(由async / await构建)在WPF应用程序的主线程上运行,但在控制台应用程序的子线程上运行

来自分类Dev

任务的延续(由async / await构建)在WPF应用程序的主线程上运行,但在控制台应用程序的子线程上运行

来自分类Dev

在传递给Task.WhenAll()的任务上使用ConfigureAwait(false)

来自分类Dev

Windows Task Scheduler未在事件上运行任务

来自分类Dev

使用任务延续时如何避免过度嵌套的异常

来自分类Dev

处理Task.WaitAll中已取消的任务和任务异常?

来自分类Dev

WaitAll之后再次触发任务

来自分类Dev

WaitAll用于更改列表<任务>

来自分类Dev

是什么导致计划任务执行延迟?

来自分类Dev

使用Task.Start触发任务时卡在Task.WaitAll(tasks.ToArray())

来自分类Dev

我希望按钮仅执行任务,执行php代码

来自分类Dev

计划的SSIS任务挂在执行流程任务上

来自分类Dev

在Middleman构建执行上运行gulp任务

来自分类Dev

是否可以在Namenode上执行任务?

来自分类Dev

在子项目上执行gradle任务

Related 相关文章

热门标签

归档