可用于任务的线程的最大数量

11

我正在努力理解C#中的异步等待功能。我编写了以下代码以异步运行多个任务 - 目前,它们只是在一定时间后引发事件。

public class Program
{
    public static Stopwatch Watch = new Stopwatch();

    public static void Main(string[] args)
    {
        AsyncClass asyncClass = new AsyncClass();
        asyncClass.WaitSecondsAsyncCompleted += asyncClass_WaitSecondsAsyncCompleted;
        List<Task> tasks = new List<Task>();
        Watch.Start();
        for (int i = 1; i < 6; i++)
        {
            tasks.Add(asyncClass.WaitSecondsAsync(i, Watch));
        }
        Task.WaitAll(tasks.ToArray());
        Console.ReadLine();
    }

    private static void asyncClass_WaitSecondsAsyncCompleted(int i)
    {
        Console.WriteLine("{1} : Async Method Called: waited for {0} seconds", i, Watch.ElapsedMilliseconds);
    }
}

public class AsyncClass
{
    public event Action<int> WaitSecondsAsyncCompleted;

    public async Task WaitSecondsAsync(int x, Stopwatch watch)
    {
        await Task.Run(() =>
        {   
            Thread.Sleep(x * 500); 
        });

        if (WaitSecondsAsyncCompleted != null)
        {
            WaitSecondsAsyncCompleted(x);
        }
    }
}

我期望一个任务每半秒钟运行一次,但实际情况并非如此。前四个任务按时完成,但最后一个任务会多延迟半秒钟:

above code输出结果

这看起来很奇怪——我唯一想到的就是任务可用线程数量有限,限制很小,所以第五个任务必须等待第一个任务完成才能开始。

我增加了一些额外的输出并增加了任务数量,试图获得更多信息,但我几乎无法理解它——输出似乎是确定性的,一些线程被重用,但也使用了新的线程。任务完成的延迟时间似乎也继续增长(例如对于任务10,我希望在5秒后完成,但实际上在8秒后停止)。以下是输出:

附带额外调试信息的输出

我想知道:

  • 有人知道这个特定示例中发生了什么吗?
  • 线程数的限制是否足够小以在这里产生影响?
  • 我认为异步任务不能保证立即启动,但这里似乎还有其他确定性过程,我没有预料到。 有人知道是什么吗?

注意,此问题并不涉及可以运行的最大任务数(TPL中的最大任务数?),而是运行仅有5个任务时如何看到效果。 我曾以为默认的线程池包含更多线程。


1
可能是 TPL 中的最大任务数? 的重复问题。 - Leo
是的,它确实有4个核心 - 请注意我发布的第二张屏幕截图,尽管最初似乎仅限于4个线程,但该数字稍后会增加,以便更多任务同时运行。 - C. Knight
请注意,您正在使用 Task.WaitAll(tasks.ToArray()); 等待任务完成,但据我所知,您并没有 等待 任务。 - Maarten
1
另外,别管线程了。它们只是一种实现细节。异步操作有时可以在没有额外线程的情况下运行。阅读名为“没有线程”的文章。 - Maarten
@C.Knight 异步操作的关键不在于它们不需要线程,而是你可以模拟代码的并行执行。例如,在从数据库获取数据时,你无需使用线程(在等待数据库时),这就是异步操作发挥作用的时候。 - Alexander Derck
显示剩余5条评论
1个回答

8

为了完整起见,我想补充一下,在大多数情况下,不建议使用SetMaxThreads来设置线程池大小。此设置是每个进程的:其他进程不受影响,但整个应用程序会受到影响。 许多TPL原语(PLINQ、Parallel.*)允许您设置最大并行度。 向此SO答案致敬。 - alelom

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