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

    解决方案寻找中

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

    [忘记发布了。。。]

  • 相关阅读:
    基础才是重中之重~B/S系统中的事件订阅
    将不确定变为确实~请自己搞清楚异常页面的设置方法(网上那些资料说的基本都有问题!)
    基础才是重中之重~延迟初始化
    批量替换sqlserver数据库TEXT字段类型的数据
    12554 A Special "Happy Birthday" Song!!!
    Linux socket 网络编程常用函数总结
    新浪微博Python SDK笔记——发微博(一)
    在Ubuntu上下载、编译和安装Android 4.2 最新内核源代码(Linux Kernel)
    20、SQL Server 数据修改之Update
    19、SQL Server 数据修改之Insert into
  • 原文地址:https://www.cnblogs.com/vwxyzh/p/1740858.html
Copyright © 2011-2022 走看看