查看: 724|回复: 0

[.NET源码] 在使用HttpClient做客户端调用一个API时 模拟并发调用时发生“死锁"?

发表于 2017-9-25 08:00:03
尚学堂AD

平时还是比较喜欢看书的。。但有时候遇到问题还是经常感到脑袋一蒙。。智商果然是硬伤。。

同事发现了个问题,代码如下:

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. HttpClientClass c = new HttpClientClass();
  6. while (true)
  7. {
  8. Task.Factory.StartNew(() =>
  9. {
  10. Console.WriteLine(Thread.CurrentThread.ManagedThreadId + "开始请求:" + DateTime.Now);
  11. c.BeginGetMethod();
  12. }, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);
  13. System.Threading.Thread.Sleep(10 * 1);
  14. }
  15. }
  16. }
  17. public class HttpClientClass
  18. {
  19. private static readonly HttpClient c;
  20. static HttpClientClass()
  21. {
  22. c = new HttpClient();
  23. c.Timeout = TimeSpan.FromSeconds(15);
  24. }
  25. public void BeginGetMethod()
  26. {
  27. try
  28. {
  29. var r = c.GetAsync("https://www.cnblogs.com/").Result;
  30. if (r.IsSuccessStatusCode)
  31. Console.WriteLine(Thread.CurrentThread.ManagedThreadId + "ok");
  32. else
  33. Console.WriteLine(Thread.CurrentThread.ManagedThreadId + "bad request");
  34. }
  35. catch (Exception ex)
  36. {
  37. Console.WriteLine(ex.GetType().FullName);
  38. }
  39. }
复制代码

在使用HttpClient的GetAsync请求后阻塞查询.Result“死锁了”,我们知道GetAsync内部也是一个后台线程在执行,直到获取到结果时会调用Task中的SetResult方法,然后通过.Result就能回去结果了。。

如果此处有问题,那我们假如做网站开发时,并发请求来了岂不是废掉了?!!

当然不是这样。。实际上线程池中处理任务时是存在任务队列的(不提看源码的事。。看完就忘。。)此处大概意思就是:主线程创建线程任务时,任务优先级高于后台线程创建的线程。这里的while不停的创建后台任务就导致了GetAsync方法中的后台任务一直在等啊等。。所以就发生了所谓的"死锁"。。其实是根本就没机会执行。。(没涉及到线程上下文切换,所以谈到这也能发生死锁时脑袋一蒙。。)

所以可以这么调用:

  1. Task.Factory.StartNew(() =>
  2. {
  3. while (true)
  4. {
  5. Task.Factory.StartNew(() =>
  6. {
  7. Console.WriteLine(Thread.CurrentThread.ManagedThreadId + "开始请求:" + DateTime.Now);
  8. c.BeginGetMethod();
  9. }, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);
  10. System.Threading.Thread.Sleep(10 * 1);
  11. }
  12. });
复制代码



回复

使用道具 举报