【问题标题】:How to determine which tasks completed first using Task.WaitAny()?如何使用 Task.WaitAny() 确定哪些任务首先完成?
【发布时间】:2019-08-28 05:19:01
【问题描述】:

在这段代码中,您总是得到相同的结果。我不知道为什么这些任务的ID是1,3,4。

如果您在int index = Task.WaitAny(tasks); 处设置断点并等待 2 秒,您将获得良好的结果。第一种情况的结果不同,ID分别等于1、2、3。

public class Example
{
    public static void Main()
    {
        var tasks = new Task[3];
        var rnd = new Random();
        for (int ctr = 0; ctr <= 2; ctr++)
            tasks[ctr] = Task.Run( () => Thread.Sleep(rnd.Next(500, 3000)));

        try 
        {
            int index = Task.WaitAny(tasks);
            Console.WriteLine("Task #{0} completed first.\n", tasks[index].Id);
            Console.WriteLine("Status of all tasks:");

            foreach (var t in tasks)
                Console.WriteLine("   Task #{0}: {1}", t.Id, t.Status);
        }
        catch (AggregateException) 
        {
            Console.WriteLine("An exception occurred.");
        }
    }
}

// The example displays output like the following:
//     Task #1 completed first.
//     
//     Status of all tasks:
//        Task #3: Running
//        Task #1: RanToCompletion
//        Task #4: Running

【问题讨论】:

标签: c# multithreading task


【解决方案1】:

如果您查看Task.Iddocumentation,它会说:

任务 ID 是按需分配的不一定代表 创建任务实例的顺序。请注意,虽然 冲突非常罕见,任务标识符不保证是 独一无二。

(强调我的)

因此,为此目的使用Task.Id(即检查首先完成的任务)绝不是可靠的。你应该做的是依赖数组中元素的索引。在这种情况下,您的代码将如下所示:

var tasks = new Task[3];
var rnd = new Random();
for (int ctr = 0; ctr <= 2; ctr++)
    tasks[ctr] = Task.Run(() => Thread.Sleep(rnd.Next(500, 3000)));

try
{
    int index = Task.WaitAny(tasks);
    Console.WriteLine("Task #{0} completed first.\n", (index + 1));
    Console.WriteLine("Status of all tasks:");

    for (int i = 0; i <= 2; i++)
        Console.WriteLine("   Task #{0}: {1}", (i + 1), tasks[i].Status);
}
catch (AggregateException)
{
    Console.WriteLine("An exception occurred.");
}

这样,当您多次运行程序时,您会得到不同的结果。你可以try it online

更多关于Task.Id行为的信息,你可以阅读Stephen Cleary的文章:A Few Words on Task.Id (and TaskScheduler.Id)

【讨论】:

  • 好的,但是在这个程序中,开始的 id 是 1,2,3,并且在“int index = Task.WaitAny(tasks);”之后它在 1,3,4 发生变化。再次写入总是首先完成任务#1。永远不要改变结果。
  • 这个答案的全部意义在于告诉您不要将Task.Id 用于此目的因为它不起作用。你为什么还在使用它并试图让它发挥作用?
  • "This write again always Task #1 completed first" 你说的“this”是什么意思?如果您指的是我在答案中编写的确切代码,那么如果您多次运行它,它应该会显示不同的结果。您是否尝试使用我提供的链接在线运行它?
  • 好的。我的第一个问题是我不理解它或它不起作用。然后我问为什么这样工作。没有人写它不工作。谢谢你的帮助。
  • 好吧,抱歉,我检查了 3 次并获得 #1。所以问题是 ID 不能正常工作。
【解决方案2】:

您应该考虑使用 Microsoft 的响应式框架(又名 Rx) - NuGet System.Reactive 并添加 using System.Reactive.Linq; - 然后您可以这样做:

var rnd = new Random();

IObservable<int> query =
    from n in Observable.Range(0, 3)
    from _ in Observable.Start(() => Thread.Sleep(rnd.Next(500, 3000)))
    select n;

IDisposable results =
    query.Subscribe(n => Console.WriteLine("Observable #{0} completed.", n));

我得到这样的输出:

Observable #0 已完成。 Observable #2 完成。 Observable #1 完成。

它通常比任务更强大,更具表现力。

【讨论】:

  • 感谢您的建议。我只是尝试了解 Tast 的工作原理,它看起来像错误。究竟是什么。
  • @mdlejtecole - 有什么错误?
  • 上面有结果。如果你使用 id 你总是有不好的结果。未结束的任务 ID 为 +1。总是赢得 id 为 1 的任务。
  • @mdlejtecole - 这不是错误,而是它的设计方式。
  • 所以完全没用,你不能检查任务是否首先通过ID结束,你不能通过ID引用,因为它们是不同的。你知道使用这个ID的例子吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-27
  • 1970-01-01
  • 1970-01-01
  • 2021-06-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多