C#趟坑: Wait()线程结束时,会忽略子线程

在我们的认知里,调用parent.Wait() 时,会等待它的子线程都结束,才会向下执行。

比如,一个线程A有B、C两个子线程,A.Wait() 是等待 A、B、C都结束,才会向下执行。

然而,最近碰到的问题却跟我想的不一样。

问题表象

可以简化为,在父线程中,创建一个子线程。然后在外部等待父线程结束。如下代码:

    var parentTask =Task.Run(()=>
    {
        Print("Parent")
        var subTask = new Task(() =>
        {
            Print("Sub")
        }, TaskCreationOptions.AttachedToParent);
        subTask.Start();
    });
    parentTask.Wait();
    Print("Parent End");

按说,parentTask.Wait()会等待父线程、子线程都结束,才会向下执行。即,期望输出  Parent   Sub   Parent End

但结果却是,Wait并没有等待子线程结束就向下执行了。 实际输出  Parent   ParentEnd   Sub

 

解决方案

把 Task.Run 换成 new Task 或者 Task.Factory.StartNew 即可。如下:

    var parentTask =new Task(()=> //把Task.Run替换为 new Task
    {
        Print("Parent")
        var subTask = new Task(() =>
        {
            Print("Sub")
        }, TaskCreationOptions.AttachedToParent);
        subTask.Start();
    });
    parentTask.Start();
    parentTask.Wait();
    Print("Parent End");

 

原因剖析

翻一下 Task.Run 的源码。

这个 DenyChildAttach 很可疑。

再看看它的注释,翻译一下:任何子Task 企图绑定到当前线程上时(以AttachedToParent的形式创建),会被拒绝,子Task将独立运行

在创建线程的源码中,也能看出这个枚举的作用:

当创建子线程时会判断:如果 parent.CreateOption 里指定DenyChildAttach,那么,就不会执行 parent.AddNewChild() 。

 

总结

使用 Task.Run 创建的 Task,是不允许再把其他线程添加为它的子线程的。因为Task.Run内部指定了 DenyChildAttach。

因此,在我们调用task.Wait时,也就不会等待那个没有被成功添加的子线程结束了。

posted @ 2020-10-26 16:43  Snow~Forever  阅读(652)  评论(0编辑  收藏  举报