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)方式的话,只需要添加任务的速度略快于处理的数据,整个过程就必然会出现死锁(所有的线程池线程都在跑主任务,而这些主任务又都在等它们的子任务,但是,它们的子任务又在线程池排队)。

    解决方案寻找中

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

    [忘记发布了。。。]

  • 相关阅读:
    Eclipse/STS 常用快捷键
    Git代码管理常用命令
    Git命令
    Atom python版本的切换
    robot Frame之文件上传和下载
    ride打开后,log和report置灰的解决办法
    Python2和Python3共存下使用robotframework
    selenium+python
    firefox上安装selenium ide失败
    软件测试知识点补充1
  • 原文地址:https://www.cnblogs.com/vwxyzh/p/1740858.html
Copyright © 2011-2022 走看看