zoukankan      html  css  js  c++  java
  • 学习TPL(四)

    引子

        现在要实现这样一个任务:

    • 下载一段内容(内容本身很小)
    • 这段内容在3个已知路径上存在,并且确认是完全相同的(也就是镜像)
    • 由于程序执行的网络环境不明,3个镜像的到达速度肯定有快慢

        于是,可以很简单的写出这样一段TPL的代码:

       1:  static void Test()
       2:  {
       3:      Task outer = Task.Factory.StartNew(() =>
       4:      {
       5:          Console.WriteLine("Outer started.");
       6:          CancellationTokenSource cts = new CancellationTokenSource();
       7:          CancellationToken token = cts.Token;
       8:          Task<string>[] tasks = new Task<string>[3];
       9:          tasks[0] = Task.Factory.StartNew(() => Download("http://www.cnblogs.com/vwxyzh/archive/2010/04/21/1716735.html", token), token);
      10:          tasks[1] = Task.Factory.StartNew(() => Download("http://www.cnblogs.com/vwxyzh/archive/2010/04/18/1714665.html", token), token);
      11:          tasks[2] = Task.Factory.StartNew(() => Download("http://www.cnblogs.com/vwxyzh/archive/2010/04/16/1713622.html", token), token);
      12:          int winnerIndex = Task.WaitAny(tasks);
      13:          cts.Cancel();
      14:          Console.WriteLine(tasks[winnerIndex].Result);
      15:      });
      16:  }
      17:   
      18:  static string Download(string url, CancellationToken token)
      19:  {
      20:      Thread.Sleep(1);
      21:      WebClient client = new WebClient();
      22:      ManualResetEvent mre = new ManualResetEvent(false);
      23:      DownloadStringCompletedEventArgs arg = null;
      24:      client.DownloadStringCompleted += (sender, e) =>
      25:          {
      26:              arg = e;
      27:              mre.Set();
      28:          };
      29:      token.Register(() => client.CancelAsync());
      30:      client.DownloadStringAsync(new Uri(url));
      31:      mre.WaitOne();
      32:      if (arg.Cancelled)
      33:          return null;
      34:      if (arg.Error != null)
      35:          throw arg.Error;
      36:      return arg.Result.Remove(100) + "...\r\nfrom " + url;
      37:  }

        在Main中执行Test方法可以看到:

    Outer started.
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
    <head>
    <meta http-equiv="C...
    from http://www.cnblogs.com/vwxyzh/archive/2010/04/18/1714665.html

        当然每次返回的网页并不一定都是这个,因为这里只是抓取了第一个返回的页面,其他页面的信息则被直接丢弃了。

        是不是看其来不错。

    问题

        别开心的太早了,这个方式仅仅是看起来很美,如果把Main函数写成这样:

       1:  for (int i = 0; i < 100; i++)
       2:      Test();

        在跑一下,看看发生了什么:

    Outer started.
    Outer started.
    Outer started.
    Outer started.
    Outer started.
    Outer started.
    Outer started.
    Outer started.
    Outer started.
    Outer started.
    Outer started.
    Outer started.
    Outer started.
    Outer started.
    Outer started.
    Outer started.
    Outer started.
    Outer started.
    Outer started.

        只看到了外面的父任务在不断的添加进来,而真正的Download过程反而一个都没跑!直到到达线程池的最大线程数,或者i的最大值。事实上,如果使用while(true)方式的话,只需要添加任务的速度略快于处理的数据,整个过程就必然会出现死锁(所有的线程池线程都在跑主任务,而这些主任务又都在等它们的子任务,但是,它们的子任务又在线程池排队)。

    解决方案寻找中

        由于方法的外界对线程池的使用方式是这个方法所不知道的,所以任何依赖线程池的动作都有可能被某些极限情况造成死锁。所以,目前暂时还没想到好的解决方案,等待大家开动脑筋,发现解决之道。

    [忘记发布了。。。]

  • 相关阅读:
    几个常见的在线评测系统,及我的点评
    信息学奥赛培训教材推荐
    致,青春
    文明小博客,管理员及网址列表
    NOIP2013,复赛及同步赛,报名及比赛,专题页面
    浅谈浏览器兼容性问题-(1)产生、看待与思考
    前端经典笔试题(腾讯前端,三栏布局)
    浅谈web语义化
    浅谈表现与数据分离
    浅谈w3c标准
  • 原文地址:https://www.cnblogs.com/vwxyzh/p/1740858.html
Copyright © 2011-2022 走看看