2012-01-15 95 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