使用或不使用线程池,是否可扩展的异步方法

时间:2018-10-19 20:27:34

标签: c# asynchronous threadpool

能否请您在这里帮助我了解两种方法之间的区别:

1)为什么第一个不使用线程池而第二个不使用线程池?

2)为什么第二个为您提供可伸缩性,但第一个却不提供-这与线程池有关吗?

3)我怎样才能在总体上调用这两种方法以使它们的目的更明显(更好地突出它们的区别)?

4)我知道可以通过await调用异步Task方法,像这些方法那样缺少异步的Task呢?

public class _12_MyClass
{

    public Task SleepAsyncA(int millisecondsTimeout)
    {
        return Task.Run(() => Thread.Sleep(millisecondsTimeout));
    }

    public Task SleepAsyncB(int millisecondsTimeout)
    {
        TaskCompletionSource<bool> tcs = null;
        var t = new Timer(delegate { tcs.TrySetResult(true); }, null, -1, -1);

        tcs = new TaskCompletionSource<bool>(t);
        t.Change(millisecondsTimeout, -1);

        return tcs.Task;
    }

    public static void Main()
    {
    }
}

1 个答案:

答案 0 :(得分:0)

这个问题很难回答,因为它比较了苹果和橙子,声称它们是水果,并试图将它们相互比较。

  

1)为什么第一个不使用线程池,第二个不使用线程池?**

     

2)为什么第二个为您提供可伸缩性,但第一个却没有-   这与线程池有关吗?

  • 默认情况下,诸如TaskTask<TResult>之类的 TPL 类型使用线程池线程来运行任务。 SleepAsyncA启动一个任务,然后调用Thread.Sleep,此操作什么都不做,只是在一段时间内阻塞了当前线程。结果,您通过窃取线程池线程并将其阻塞直到完成才成为计时器。显然,如果保持这种设计规模不变,线程池线程就会用光。

  • SleepAsyncB在计时器上工作。 System.Threading.Timer在线程池线程上调度回调,而不是每次都创建一个新线程。在这种情况下,它只是暂时在TaskCompletionSource上设置结果。

    •   

      TaskCompletionSource<TResult>类型有两个相关用途,   它是创建任务的来源,而该任务的来源   完成。 TaskCompletionSource<TResult>充当生产者   Task<TResult>及其完成。与创建的任务不同   Task.Run等,由   Task没有任何预定的委托   与之相关联,但是提供了允许您控制的方法   任务的寿命和完成情况。

    • 基本上,您正在采用TaskCompletionSource<TResult>产生的事件模式,并使用Timer使它像TaskCompletionSouce一样工作。 timout不会阻塞任何线程,因此更具伸缩性。

  

3)我如何才能调用main中的两个方法以使其更有效   明显(更好地突出了它们之间的差异)?

给出

Task

用法

public static string DebugInfo
{
   get
   {
      ThreadPool.GetMaxThreads(out var maxThreads, out _);
      ThreadPool.GetAvailableThreads(out var threads, out _);
      var usedThreads = maxThreads - threads;
      var mt = $"{usedThreads.ToString().PadLeft(4)}/{maxThreads.ToString().PadLeft(4)}";
      return $"Threads {mt.PadRight(8)}";
   }
}
public static Task SleepAsyncA(int millisecondsTimeout)
{
   return Task.Run(() => { Console.WriteLine("SleepAsyncA " + DebugInfo);  Thread.Sleep(millisecondsTimeout); });
}

public static Task SleepAsyncB(int millisecondsTimeout)
{
   TaskCompletionSource<bool> tcs = null;
   var t = new Timer(delegate { tcs.TrySetResult(true); }, null, -1, -1);

   Console.WriteLine("SleepAsyncB " + DebugInfo);

   tcs = new TaskCompletionSource<bool>(t);
   t.Change(millisecondsTimeout, -1);

   return tcs.Task;
}

输出

   var ms = 5000;
   Console.WriteLine("Start " + DebugInfo);
   var list = Enumerable.Range(0, 10).Select(x => SleepAsyncA(ms));

   Task.WaitAll(list.ToArray());

   var list2 = Enumerable.Range(0, 10).Select(x => SleepAsyncB(ms));
   Task.WaitAll(list2.ToArray());

Full Demo Here

  

4)我知道可以通过await调用异步Task方法,那又如何呢?   像这些方法那样缺少异步的任务?

当然!它是避免多余状态机并为您提供效率的常用模式。但是,每次调用Start Threads 0/2047 SleepAsyncA Threads 8/2047 SleepAsyncA Threads 8/2047 SleepAsyncA Threads 8/2047 SleepAsyncA Threads 8/2047 SleepAsyncA Threads 8/2047 SleepAsyncA Threads 8/2047 SleepAsyncA Threads 8/2047 SleepAsyncA Threads 8/2047 SleepAsyncA Threads 9/2047 SleepAsyncA Threads 10/2047 SleepAsyncB Threads 0/2047 SleepAsyncB Threads 0/2047 SleepAsyncB Threads 0/2047 SleepAsyncB Threads 0/2047 SleepAsyncB Threads 0/2047 SleepAsyncB Threads 0/2047 SleepAsyncB Threads 0/2047 SleepAsyncB Threads 0/2047 SleepAsyncB Threads 0/2047 SleepAsyncB Threads 0/2047 时,您的方法都必须标记为await

async

最后,为什么不保存所有麻烦和多余的代码,而只使用private static async Task Main(string[] args) { var ms = 5000; await SleepAsyncA(ms); await SleepAsyncB(ms); }

Task.Delay(ms)

可扩展,简单,大惊小怪。