C#多线程中,如何判断所有task执行完毕?

gyy9911 2018-12-14 09:02:41
我尝试使用C#的task对快速排序算法进行并行化,简单的思路是:确定枢值位置后将前后两段分别交给两个不同的task并发执行,如此递归,最终能得到正确的排序结果
我想对算法进行计时,并将结果保存在文件中,但是无法确定排序究竟何时结束,由于不停地有新的task被创建,也很难用waitall()之类的常规手段来判断
有没有方法可以判断当前所有task是否全部执行完毕?
...全文
4523 17 打赏 收藏 转发到动态 举报
写回复
用AI写文章
17 条回复
切换为时间正序
请发表友善的回复…
发表回复
adandelion 2019-01-21
  • 打赏
  • 举报
回复
使用 task 来汇合多个任务时,其实是使用 Task.WhenAll 更好
  • 打赏
  • 举报
回复
实际上,使用 task 来汇合多个任务时,其实是使用 Task.WhenAll 更好。因为那是 Task,是非阻塞的。只不过你只是问了 WaitAll 阻塞语句,我上面没有用 WhenAll 方式。
生财 2018-12-16
  • 打赏
  • 举报
回复
可以使用,反应堆模型,用一个或者几个线程一直不停的运行,然后其它任务都放到这个线程里,任务执行完成后,以事件方式通知任务执行完成。
橘子皮... 2018-12-16
  • 打赏
  • 举报
回复
引用 7 楼 以专业开发人员为伍 的回复:
这样来解释 task 吧,当 底层的 Task.Run 执行完毕时,上一层的 t1 或者 t2 任务才会完毕,这样上一层的 Task.WaitAll 就可以阻塞到嵌套底层的 Task.Run 任务实际结束。如此递归可知,顶层的 WaitAll 就会等到底层嵌套产生的所有 Task 结束才结束。

Dim pd As Boolean = False

    Sub Main()
        Dim L As New List(Of Task)

        Console.WriteLine("Start   " & TimeOfDay)
        Dim t = Task.Factory.StartNew(Sub()

                                          Task.Factory.StartNew(Sub()
                                                                    Do
                                                                        Thread.Sleep(1000)
                                                                    Loop
                                                                End Sub, Tasks.TaskCreationOptions.AttachedToParent)

                                          Do
                                              Thread.Sleep(100)
                                          Loop Until pd

                                      End Sub)
        L.Add(t)

        Thread.Sleep(2000) : pd = True

        Task.WaitAll(L.ToArray)

        Console.WriteLine("End   " & TimeOfDay)
        Console.Read()
    End Sub
感觉不是啊,如果我不加Tasks.TaskCreationOptions.AttachedToParent的话,2秒后直接就提示End表示线程t结束了,但是他的子线程其实还在死循环中呢。 如果想不加也判断子子线程都结束的话,可以这样
Dim pd As Boolean = False

    Sub Main()
        Dim L As New List(Of Task)
        Dim tt As Task

        Console.WriteLine("Start   " & TimeOfDay)
        Dim t = Task.Factory.StartNew(Sub()

                                          tt = Task.Factory.StartNew(Sub()
                                                                         Do
                                                                             Thread.Sleep(1000)
                                                                         Loop
                                                                     End Sub)
                                          L.Add(tt)

                                          Do
                                              Thread.Sleep(100)
                                          Loop Until pd

                                      End Sub)
        L.Add(t)

        Thread.Sleep(2000) : pd = True

        Task.WaitAll(L.ToArray)

        Console.WriteLine("End   " & TimeOfDay)
        Console.Read()
    End Sub
gyy9911 2018-12-16
  • 打赏
  • 举报
回复
引用 7 楼 以专业开发人员为伍 的回复:
这样来解释 task 吧,当 底层的 Task.Run 执行完毕时,上一层的 t1 或者 t2 任务才会完毕,这样上一层的 Task.WaitAll 就可以阻塞到嵌套底层的 Task.Run 任务实际结束。如此递归可知,顶层的 WaitAll 就会等到底层嵌套产生的所有 Task 结束才结束。
谢谢,我懂了,我原本想的是如何判断那几万个线程全部结束,现在就容易多了
gyy9911 2018-12-16
  • 打赏
  • 举报
回复
引用 2 楼 以专业开发人员为伍 的回复:
既然你说“将前后两段分别交给两个不同的task并发执行”,又说什么“很难用waitall()之类的常规手段”,这是什么意思?两个task你就不敢用 waitall了?
30000个数递归调用排序算法,最后会创建几万个task,我并不知道最后能有多少个task,也就没法把它们全放进waitall里 4楼说的方法我也试过,也不行
gyy9911 2018-12-16
  • 打赏
  • 举报
回复
引用 4 楼 橘子皮zzz 的回复:
这个问题我问过,我刚才想起来个办法,创建1个List(Task),吧所有创建过的task都add进List里,最后Task.Wait(List)搞定
这个方法我试过,没有用。Task.Wait(List)之后是我打印输出的代码,但每次打印的结果都是乱的,说明排序其实并没有结束,程序就已经往下运行了
橘子皮... 2018-12-16
  • 打赏
  • 举报
回复
net4.0的机器怎么用Async await ??好像在哪看到说有办法的
  • 打赏
  • 举报
回复
task 快速排序? task的执行先后顺序都不能保障,你如何确认还能用于快速排序? 而且排序这种行为会产生很大的时间消耗吗?需要用到task来做?
  • 打赏
  • 举报
回复
既然你说“将前后两段分别交给两个不同的task并发执行”,又说什么“很难用waitall()之类的常规手段”,这是什么意思?两个task你就不敢用 waitall了?
  • 打赏
  • 举报
回复
嗯,如果只有顶层记录日志,可以写:
public static void 快速排序<T>(T[] datas) where T : IComparable<T>
{
    var t0 = new Stopwatch();
    t0.Start();
    快速排序Async(datas, 0, datas.Length - 1).Wait();
    Task.Run(()=> 记录日志(datas, t0));
}
不扩散 Task 编程。 我想说的是,其实 Task 编程思想是一种自顶向下的设计思想,所以它才比较自由。如果你过多地”自底向上拼凑“可能就会纠结这类技术问题比较多。
sp1234_maJia 2018-12-15
  • 打赏
  • 举报
回复
Task(以及 async/await)编程机制具有传染性,要重视这个特性。假设一部分使用了 Task,那么会造成上级的所有代码也不得不改为 Task 的机制的。直到某个底层方法断然”中断“Task为止。 上面的伪代码的 demo 程序,顶层使用了 Wait() 方法来屏蔽了 Task。实际上假设说只有顶层才需要记录日志,而底层任意层都不需要记录日志,那么顶层可能改为
public static async void 快速排序<T>(T[] datas) where T : IComparable<T>
{
    var t0 = new Stopwatch();
    t0.Start();
    await 快速排序Async(datas, 0, datas.Length - 1);
    await 记录日志(datas, t0);
}
这就是传染性的表现。 为什么传统的 PLinq 在 Task 面前显得有点 low 呢?就是因为 PLinq 目前还是阻塞式的,所以递归分解算法时会造成大量被阻塞和滥用的线程;而 Task 程序设计是异步的、不阻塞的,这就是其高级的地方。
  • 打赏
  • 举报
回复
这样来解释 task 吧,当 底层的 Task.Run 执行完毕时,上一层的 t1 或者 t2 任务才会完毕,这样上一层的 Task.WaitAll 就可以阻塞到嵌套底层的 Task.Run 任务实际结束。如此递归可知,顶层的 WaitAll 就会等到底层嵌套产生的所有 Task 结束才结束。
  • 打赏
  • 举报
回复
我没有测试,这里只是伪代码而且也没有做任何优化,你自己修改、调试。 这里只是说明 WaitAll 的概念。任何控制只要在逻辑层做到位就行了,不要从技术底层去拼凑(不是说什么有多少个 task 需要你 WaitAll),顶多只有两个 Task 需要 WaitAll 而已。所以其实这原本是非常简单的逻辑,要从知识的顶层去写算法。
  • 打赏
  • 举报
回复
随便给你写一个例子吧。大致是这样
public static void 快速排序<T>(T[] datas) where T : IComparable<T>
{
    快速排序Async(datas, 0, datas.Length - 1).Wait();
}

public static void 快速排序Sync<T>(T[] datas, int left, int right, bool 是否需要写日志 = false) where T : IComparable<T>
{
    var t0 = new Stopwatch();
    t0.Start();
    var index = 划分(datas);
    if (index - left <= right - index)
    {
        快速排序Sync(datas, left, index - 1);
        快速排序Sync(datas, index + 1, right);
        写日志(datas, left, right, index, t0);
    }
    else
    {
        快速排序Sync(datas, index + 1, right);
        快速排序Sync(datas, left, index - 1);
        写日志(datas, left, right, index, t0);
    }
}


public static Task 快速排序Async<T>(T[] datas, int left, int right, bool 是否需要写日志 = false) where T : IComparable<T>
{
    if (right - left < 10000)
    {
        快速排序Sync(datas, left, right, 是否需要写日志);
        return Task.CompletedTask;
    }
    else
    {
        var t0 = new Stopwatch();
        t0.Start();
        var index = 划分(datas);
        if (index - left <= right - index)
        {
            var t1 = 快速排序Async(datas, left, index - 1);
            var t2 = 快速排序Async(datas, index + 1, right);
            return Task.Run(() =>
            {
                Task.WaitAll(t1, t2);
                写日志(datas, left, right, index, t0, t1, t2);
            });
        }
        else
        {
            var t2 = 快速排序Async(datas, left, index - 1);
            var t1 = 快速排序Async(datas, index + 1, right);
            return Task.Run(() =>
            {
                Task.WaitAll(t1, t2);
                写日志(datas, left, right, index, t0, t1, t2);
            });
        }
    }
首先要区分到底是否要记录日志,因为你的目的是要记录日志,这里设计了用来告知递归函数是否要记录日志的参数,你可以修改参数设计来实现你的控制方式。 函数中要判断是否值得使用并发异步算法,这里假设当规模大于1万时才使用并发异步操作,否则还是使用同步单线程算法。
橘子皮... 2018-12-15
  • 打赏
  • 举报
回复
这个问题我问过,我刚才想起来个办法,创建1个List(Task),吧所有创建过的task都add进List里,最后Task.Wait(List)搞定
threenewbee 2018-12-14
  • 打赏
  • 举报
回复
你的task是在你的排序算法内产生的,你可以递归调用waitall判断,也可以直接用parallel.for

110,533

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧