2012-01-15 99 views
0

我得到一个XML提要,并将其解析为我的MQ服务器,然后我有一个服务来监听MQ服务器并阅读其所有消息。在foreach循环中打开线程

我打开一个新的线程每次迭代中,为了使解析更快foreach循环,原因有在MQ大约500消息(意味着有500个XML)

foreach (System.Messaging.Message m in msgs) 
{ 
    byte[] bytes = new byte[m.BodyStream.Length]; 
    m.BodyStream.Read(bytes, 0, (int)m.BodyStream.Length); 
    System.Text.ASCIIEncoding ascii = new System.Text.ASCIIEncoding(); 

    ParserClass tst = new ParserClass(ascii.GetString(bytes, 0, (int)m.BodyStream.Length)); 
    new Thread(new ThreadStart(tst.ProcessXML)).Start(); 
} 

在ParserClass我有这样的代码:

private static object thLockMe = new object(); 
public string xmlString { get; set; } 

public ParserClass(string xmlStringObj) 
{ 
    this.xmlString = xmlStringObj; 
} 

public void ProcessXML() 
{ 
    lock (thLockMe) 
    { 
     XDocument reader = XDocument.Parse(xmlString); 
     //Some more code... 
    } 
} 

的问题是,当我运行这个foreach循环,只有1个线程,它可以完美运行,但速度缓慢。

当我用多于一个线程运行它时,出现错误“未将对象引用设置为对象的实例”。

我想我的锁定有问题,因为我对线程不是很有经验。

我有点无望,希望你能帮助!

干杯!

+0

哪一行引发异常? – Tudor 2012-01-15 20:51:55

+2

不要启动500个线程。 – 2012-01-15 20:54:47

+0

购买一个500核心CPU,那么它将100%并行,超级快速和无bug。 (换句话说,你的核心想法很可能是错误的,修复同步问题在一天结束时不会帮助你。) – 2012-01-15 20:57:16

回答

4

我注意到你正在运行一堆线程,它们的整个代码被包装在一个lock语句中。您也可以按照这种方式按顺序运行这些方法,因为您没有获得任何并行性。

+0

我不明白,对不起,我该怎么办? – 2012-01-15 21:08:29

+0

告诉我们在你的代码行中你会得到异常 – Tudor 2012-01-15 21:09:05

+0

我有一些用于解析xml的代码,例如:int bookieID = int.Parse(reader.Element(“EventMessage”)。Element(“Header”)。 (“BookieID”)。V alue);但是我得到“对象引用未设置为对象的实例”,并且所有这些元素都在xml中设置为100%,我尝试删除锁(..)但它仍然发生在ProcessXML函数中发生异常只有当我使用多于1个线程 – 2012-01-15 21:10:26

3

既然你在你的循环的每次迭代创建一个新的ParserClass实例,还创建并启动一个新的线程每次迭代,你并不需要在你的ParseXML方法的锁。

你在你锁定的对象是目前static,所以不是实例绑定,这意味着,一旦一个线程是你ParseXML方法里面,没有其他可以做任何事情,直到第一次完成。

您的分析器类中没有共享线程中的任何数据(来自代码我可以看到),因此您不需要锁定,就可以在您的ParseXML函数中使用。

如果您使用的是线程之间共享的数据,那么您应该有一个锁。

如果您要使用大量线程,那么您最好使用ThreadPool,并从池中取出一个有限的(可能为4个),分配给他们一些工作,并将它们循环用于接下来的4个任务。

创建线程是一项昂贵的操作,需要调用OS内核,因此您不需要这样做500次。这太昂贵了。另外,Windows中针对线程堆栈的最小预留内存为1MB,因此线程的堆栈空间中只有500MB。

线程的最佳数量应该等于您的机器中的核心数量,但由于这对大多数用途来说并不是真实的,所以您可以做到两倍或三倍,但是最好使用线程池,你回收线程,而不是一直在创造新的。

+0

嘿托尼,感谢您的重播,我有一些代码,用于解析XML。例如:int bookieID = int.Parse(reader.Element(“EventMessage”)。Element(“Header”)。Element(“BookieID”)。但我得到“对象引用未设置为对象的实例”,并且所有这些元素都在xml中设置为100%。我试图删除锁(..),但它仍然发生。 – 2012-01-15 21:05:20

+0

是'reader'一个有效的对象吗?你能发布实际的代码吗? – 2012-01-15 21:12:02

+0

这是代码:http://pastebin.com/cNtZE9g3 - 我在134行得到错误。 – 2012-01-15 21:16:32

2

尽管这可能不会解决,而不是创建500个并发线程,你应该只使用线程池,管理线程在一个更有效的方式您的问题,:

foreach (System.Messaging.Message m in msgs) 
{ 
    byte[] bytes = new byte[m.BodyStream.Length]; 
    m.BodyStream.Read(bytes, 0, (int)m.BodyStream.Length); 
    System.Text.ASCIIEncoding ascii = new System.Text.ASCIIEncoding(); 

    ParserClass tst = new ParserClass(ascii.GetString(bytes, 0, (int)m.BodyStream.Length)); 
    ThreadPool.QueueUserWorkItem(x => tst.ProcessXML()); 
} 

,并确保他们运行尽可能同时更改ParserClass这样你的代码(假设你确实有资源,你的线程之间共享 - 如果你没有任何,你不必在所有的锁):

private static object thLockMe = new object(); 
public string XmlString { get; set; } 

public ParserClass(string xmlString) 
{ 
    XmlString = xmlString; 
} 

public void ProcessXML() 
{ 
    XDocument reader = XDocument.Parse(xmlString); 
    // some more code which doesn't need to access the shared resource 

    lock (thLockMe) 
    { 
     // the necessary code to access the shared resource (and only that) 
    } 

    // more code 
} 

关于你的实际问题:

而不是调用OddService.InsertEvent(...)多次使用相同的参数(远程调用和副作用的方法reeks ...),您应该调用它一次,将结果存储在变量中,并对该变量执行所有后续操作。这样,您还可以方便地检查它是否不是那种有时会返回null的精确方法(同时访问?)。

编辑:

请问,如果你把所有的呼叫,OddService.*lock块工作的呢?

+0

谢谢!我得到一个错误:参数1:无法从'方法组'转换为'System.Threading.WaitCallback'这一行:ThreadPool.QueueUserWorkItem(tst.ProcessXML); – 2012-01-15 21:21:01

+0

当我删除“锁”时,我得到了同样的错误,当我设置锁,它的工作原理,但只有迭代迭代,非常缓慢.. – 2012-01-15 21:57:04

+0

“相同”在“相同在我原来的问题”或在“与我之前的评论相同”? – Nuffin 2012-01-15 22:00:22