任务等待所有 - 没有等待?

4

我正在试图理解任务并行库中的Task.WaitAll(..)。

我正在运行一个测试,使用以下代码来模拟两个任务,一个任务将运行比等待的指定时间长(10秒),另一个任务将运行比等待的指定时间短(3秒)。指定的等待时间为5秒。我使用的代码如下:

Task<int>[] tasks = new Task<int>[]
{
    Task.Factory.StartNew<int>(()=>
        {
            Thread.Sleep(10000);
            return 1;
        }),
    Task.Factory.StartNew<int>(()=>
        {
            Thread.Sleep(3000);
            return 2;
        })
};

TimeSpan timeSpan = new TimeSpan(0, 0, 5);
Task.WaitAll(tasks,timeSpan);

int[] results = new int[tasks.Length];
for(int i = 0; i < tasks.Length;i++)
{
    Console.WriteLine(tasks[i].Result);
}

就时间跨度而言,我也尝试直接传递5000而不是timeSpan变量,但不起作用。我得到了以下输出:

1
2

我期望只得到以下输出,因为另一个线程执行的时间超过了预期的等待时间。
1

我是否误解了这个,或者测试做错了?

3
在我看来,你接受了错误的答案。 :) - James Manning
记住,有时候界面需要一些时间来绘制。 - Jerry Nixon
3个回答

11

虽然通常情况下应该使用Task.Delay(这样您不会在睡眠期间浪费线程),但您的问题实际上与此无关。

这里发生的是,当您打印结果时,您正在访问每个任务的Result属性。 这个操作阻塞了任务的完成,因此在WaitAll中等待了5秒,然后在打印10秒任务的结果时又等待了另外5秒。

根据您所述的意图,您需要在访问Result之前检查任务状态,因为您的目的不是在它上面阻塞,而只是在它已经完成时打印出来:

int[] results = new int[tasks.Length];
for (int i = 0; i < tasks.Length; i++)
{
    if (tasks[i].IsCompleted)
    {
        Console.WriteLine(tasks[i].Result);
    }
}

顺便说一句,你可以通过添加一些简单的时间检查来展示“Result上的阻塞”是发生在原始代码中的(例如,使用Stopwatch,如下所示)。

Task<int>[] tasks = new Task<int>[]
{
    Task.Factory.StartNew<int>(()=>
        {
            Thread.Sleep(10000);
            return 1;
        }),
    Task.Factory.StartNew<int>(()=>
        {
            Thread.Sleep(3000);
            return 2;
        })
};

TimeSpan timeSpan = new TimeSpan(0, 0, 5);
var stopwatch = Stopwatch.StartNew();
Task.WaitAll(tasks, timeSpan);
Console.WriteLine("WaitAll took {0} seconds", stopwatch.Elapsed.TotalSeconds);

int[] results = new int[tasks.Length];
for (int i = 0; i < tasks.Length; i++)
{
    stopwatch = Stopwatch.StartNew();
    Console.WriteLine(tasks[i].Result);
    Console.WriteLine("Printing result took {0} seconds", stopwatch.Elapsed.TotalSeconds);
}

这将产生以下控制台输出:

WaitAll took 4.9996961 seconds
1
Printing result took 5.0050012 seconds
2
Printing result took 0.0004338 seconds

3

在任务中不要使用Thread.Sleep。 Task 调度程序不能保证每个任务一个线程,并且睡眠可能会影响其他任务(请参见 Task is ignoring Thread.Sleep)。请改用 Task.Delay()


1
这只有在4.5版本中可用吗? - TheJediCowboy
@CitadelCSAlum 很遗憾,Task.Delay() 仅适用于 .Net 4.5。 - akton
3
切换为 Task.Delay 并不能解决此处的问题,因为访问 Result 属性会阻塞直到完成了 10 秒任务。 - James Manning

0

尝试将您的时间跨度转换为

TimeSpan span = new TimeSpan(0, 0, 0, 10, 0);
//TimeSpan(Days, hours, minutes, seconds, milliseconds);

那不应该是问题。TimeSpan的构造函数需要三个参数,分别为小时、分钟和秒。链接 - svick
1
更不用说,如果我要改变它的创建方式,那就是使用 TimeSpan.FromSeconds(10) :) - James Manning

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接