none
多个线程如何维护同一个集合 RRS feed

  • 问题

  • 在做一个蜘蛛程序,有一个集合中存放着所有等待抓取的URL地址,现在我想开二十个线程,同时抓取,我怎么样能让这二十个线程,去访问同一个集合,而拿的数据又不一样呢??还有,我要把处理的结果放在同一个集合中。谢谢!

    如果我这样写能不能保证不同的线程来读,不会重复,其中的Links是当前这个类的属性,当开启线程的时候,当前线程会不会复制一个IList?这个IList是在工作内在中还是在主内存中呢?

    public string GetLinks()
    
     {
    
      try
    
      {
    
       string link = "";
    
       Monitor.Enter(Links);
    
       if (Links.Count > 0)
    
       {
    
        link = Links[0];
    
        Links.RemoveAt(0);
    
       }
    
       return link;
    
      }
    
      catch (Exception ex)
    
      {
    
       return "";
    
      }
    
      finally
    
      {
    
       Monitor.Exit(Links);
    
      }
    
     }
    
    

     如果我有10个线程,我怎么样判断他们是否全部完成任务?

    2010年6月11日 1:22

答案

  • 你好,

    不太理解你是怎么调用这个方法的。

    你每次调用这个方法的时候都创建了一个wrfor对象,那你这还锁住它没有什么意义啊。

    锁住的应该是公共的,都需要访问的。

     


    Microsoft Online Community Suppor

     

    我开始也以为每次创建一个新的对象不用LOCK可是我线程多了以后就报错,

    我后来测试了一下,如果两个不同的线程去调用同一个方法的时候,他俩的数据是同步的,也就是第一个线程更改了对象以后,第二个线程过来这个对象是更改后的内容了,比如有两个线程一前一后,这个对象刚刚关闭了流,结果第二个刚要读,这时候就报了异常了


    出现这种情况是因为您的 lock 语句写法不当造成的。请参考 Kefeng Chen (MSFT) 的回答,以及我之前的回复,重新写一下 lock。再次警告,不要试图 lock 本地变量,要 lock 静态变量,否则,lock 并不起作用的。

    public class Foo
    {
        public static object SyncObject = new object();
        public void Bar()
        {
            lock (Foo.SyncObject) { ... }
        }
    }

    对于集合类型,如果有 SyncRoot 属性的,要 lock SyncRoot 属性,而不是集合本身


    Mark Zhou
    2010年6月15日 7:24

全部回复

  • 1.通常的办法是加锁,避免资源竞争就可以。

    2.你拿完数据后,就把这个数据remove了,就可以保证不会被其他进程拿走。

    不过以我个人建议:我通常是搞一个调度去分配需要爬行的网站。为什么这样呢?因为分配过程是很快的,而爬虫爬网的慢,所以可以一个分配,而多个线程爬网。

    当然还存在多个线程爬网后,发现的新的url如何加入的问题,这个是新增list内容,还是上面说的加锁就好了。

     

     


    family as water
    2010年6月11日 2:00
  • 我上面那种写法可以吗??
    2010年6月11日 3:24
  • 我上面那种写法可以吗??


    不可以。lock 集合对象时,特别是 List,Dictionary 等,不能直接锁定对象本身,而是对象的 SyncRoot 属性。请修改 Monitor.Enter 代码。

    另外,建议直接用 lock 关键字,这样代码更加易读。


    Mark Zhou
    2010年6月11日 9:36
  • 当我用10个以上线程运行,执行下面这个方法的时候总是提示流已经关闭

      public string GetHtml(string url)
      {      
            WebRequest wrfor = WebRequest.Create(url);
            lock (wrfor)
            {
              WebResponse hwrfor = wrfor.GetResponse();
              Stream sfor = hwrfor.GetResponseStream();
              StreamReader srfor = new StreamReader(sfor, System.Text.Encoding.Default);
              string Htmlfor = srfor.ReadToEnd();
              srfor.Close();
              hwrfor.Close();
              return Htmlfor;
            }
      }
    就是下面这两个Close怎么处理,单独的线程执行这个方法的时候不是在自己的工作内存中执行吗?为什么当A线程关闭了这个srfor.Close();
    ,B线程也就用不了了,是我理解的有问题吗?当两个线程分别执行WebRequest wrfor = WebRequest.Create(url);这一句的时候,线程不是创建的新的对象吗??
    2010年6月11日 12:45
  • 你好,

    不太理解你是怎么调用这个方法的。

    你每次调用这个方法的时候都创建了一个wrfor对象,那你这还锁住它没有什么意义啊。

    锁住的应该是公共的,都需要访问的。


    Microsoft Online Community Support
    2010年6月14日 3:27
  • 你好,

    不太理解你是怎么调用这个方法的。

    你每次调用这个方法的时候都创建了一个wrfor对象,那你这还锁住它没有什么意义啊。

    锁住的应该是公共的,都需要访问的。


    Microsoft Online Community Suppor

    我开始也以为每次创建一个新的对象不用LOCK可是我线程多了以后就报错,

    我后来测试了一下,如果两个不同的线程去调用同一个方法的时候,他俩的数据是同步的,也就是第一个线程更改了对象以后,第二个线程过来这个对象是更改后的内容了,比如有两个线程一前一后,这个对象刚刚关闭了流,结果第二个刚要读,这时候就报了异常了

    2010年6月14日 11:08
  • 你好,

    不太理解你是怎么调用这个方法的。

    你每次调用这个方法的时候都创建了一个wrfor对象,那你这还锁住它没有什么意义啊。

    锁住的应该是公共的,都需要访问的。

     


    Microsoft Online Community Suppor

     

    我开始也以为每次创建一个新的对象不用LOCK可是我线程多了以后就报错,

    我后来测试了一下,如果两个不同的线程去调用同一个方法的时候,他俩的数据是同步的,也就是第一个线程更改了对象以后,第二个线程过来这个对象是更改后的内容了,比如有两个线程一前一后,这个对象刚刚关闭了流,结果第二个刚要读,这时候就报了异常了


    出现这种情况是因为您的 lock 语句写法不当造成的。请参考 Kefeng Chen (MSFT) 的回答,以及我之前的回复,重新写一下 lock。再次警告,不要试图 lock 本地变量,要 lock 静态变量,否则,lock 并不起作用的。

    public class Foo
    {
        public static object SyncObject = new object();
        public void Bar()
        {
            lock (Foo.SyncObject) { ... }
        }
    }

    对于集合类型,如果有 SyncRoot 属性的,要 lock SyncRoot 属性,而不是集合本身


    Mark Zhou
    2010年6月15日 7:24