【问题标题】:Do I need to worry about blocking tasks?我需要担心阻塞任务吗?
【发布时间】:2013-02-26 09:09:10
【问题描述】:

我需要为 .NET 中的阻塞任务担心多少?即 .NET 任务调度程序如何处理线程池中的线程阻塞和超额订阅?

例如如果我在任务中有一些 IO,我是否应该始终使用 LongRunning 提示创建它?还是任务调度程序启发式处理它更好?在 C++ 中有一个 Oversubscribe 提示可以完美运行,但我在 .NET 中没有找到任何等效项。

【问题讨论】:

    标签: c# .net io task-parallel-library task


    【解决方案1】:

    ThreadPool 确实会检测到它的一个线程何时阻塞,并提示它向池中添加另一个线程。所以,如果你阻塞很多,性能很可能不会很糟糕,因为 ThreadPool 会尝试让你的 CPU 内核保持忙碌。

    但是有许多阻塞的线程可能是一个性能问题,因为它会增加内存消耗并可能导致更多的上下文切换。

    此外,这种行为可能会导致 IO 性能下降。使用旋转磁盘 (HDD),同时访问多个文件会导致大量查找,这会极大地影响性能。

    【讨论】:

      【解决方案2】:

      如果您想要最高性能的代码,您确实需要担心它。

      处理它的最好方法是使用the .Net 4.5 "await" style of I/O

      如果您没有 .Net 4.5,则必须使用 the older style of I/O(同样有效,但更难使用)。

      这些文章中描述的非阻塞 I/O 是迄今为止使用多线程执行 I/O 的最佳方式。

      如果您不使用 I/O,那么您仍然可以从这些文章中学到很多东西。

      【讨论】:

      • 我尽可能使用 await。但是我需要调用一些我不知道它们会做什么的遗留 API,IO、Thread.Sleep 等等……
      • @ronag 如果性能对您很重要,您应该考虑长期将这些遗留 API 重写为异步。
      • @svick:在这种情况下,不可能而且可能不值得。我只需要知道整个线程池是否会被阻塞,或者它是否会在需要时自动将新线程注入池中。
      【解决方案3】:

      LongRunning 向 TPL 发出信号 使用线程池线程——它创建一个非线程池线程来满足请求(例如new Thread(...))。这不是你应该为 IO 做的事情。您应该使用异步 IO。例如:

      using(var response = (HttpWebResponse)await WebRequest.Create(url).GetResponseAsync())
          return response.StatusCode == HttpStatusCode.OK;
      

      这样可以确保尽可能使用重叠的 IO——它使用 IO 线程池。

      如果您想将 Task 与旧版 APM API 一起使用,您可以使用 FromAsync:

      Task<int> bytesRead = Task<int>.Factory.FromAsync( 
          stream.BeginRead, stream.EndRead, buffer, 0, buffer.Length, null);
      await bytesRead;
      

      如果需要处理遗留事件异步 API,可以使用 TaskCompletionSource:

      TaskCompletionSource<string[]> tcs = new TaskCompletionSource<string[]>();
      WebClient[] webClients = new WebClient[urls.Length];
      object m_lock = new object();
      int count = 0;
      List<string> results = new List<string>();
      
      for (int i = 0; i < urls.Length; i++)
      {
          webClients[i] = new WebClient();
      
          // Specify the callback for the DownloadStringCompleted 
          // event that will be raised by this WebClient instance.
          webClients[i].DownloadStringCompleted += (obj, args) =>
          {
              // Argument validation and exception handling omitted for brevity. 
      
              // Split the string into an array of words, 
              // then count the number of elements that match 
              // the search term. 
              string[] words = args.Result.Split(' ');
              string NAME = name.ToUpper();
              int nameCount = (from word in words.AsParallel()
                               where word.ToUpper().Contains(NAME)
                               select word)
                              .Count();
      
              // Associate the results with the url, and add new string to the array that  
              // the underlying Task object will return in its Result property.
              results.Add(String.Format("{0} has {1} instances of {2}", args.UserState, nameCount, name));
      
              // If this is the last async operation to complete, 
              // then set the Result property on the underlying Task. 
              lock (m_lock)
              {
                  count++;
                  if (count == urls.Length)
                  {
                      tcs.TrySetResult(results.ToArray());
                  }
              }
          };
      
          // Call DownloadStringAsync for each URL.
          Uri address = null;
          address = new Uri(urls[i]);
          webClients[i].DownloadStringAsync(address, address);
      
      } // end for 
      
      await tcs.Task;
      

      【讨论】:

      • 我尽可能使用 await。但是我需要调用一些我不知道它们会做什么的遗留 API,IO、Thread.Sleep 等等……
      • 您可以放心,没有框架 API 会调用值大于 1 的 Thread.Sleep...
      • 我已经通过回答更新了一些使用“遗留”api 的示例。
      猜你喜欢
      • 1970-01-01
      • 2015-09-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-28
      相关资源
      最近更新 更多