2012-06-15 50 views
0

我的主程序使用Task.Factory.StartNew運行8個任務如何異步解析來自HttpWebRequest的XML?

每個任務都會從webservice請求XML格式的結果,然後解析爲可以使用TVP寫入MSSQL的集合。

該程序的工作原理,但使用TPL的效率增益並非我所期望的。在各種情況下使用秒錶後,在我看來,這些任務正在相互干擾,也許一個阻塞另一個。所有數字都指向使用HttpWebRequest的下載部分。

在c#中的異步編程中搜索並閱讀了一點之後,我試圖修改我的代碼以異步運行下載部分,但結果仍然顯示相似級別的阻止,而不使用異步編碼。

有3種類型的代碼,我發現了幾個參考它們的鏈接:

How to use HttpWebRequest (.NET) asynchronously? - 當我用這個方法我通過了周圍的XDocument在下載部分方法,使用自定義對象

使用C#

異步編程迭代 http://tomasp.net/blog/csharp-async.aspx -string /流被返回並在主方法,使用XDocument.Load /解析解析

下面的代碼塊顯示發現,在我的代碼實現

主要類的最後一個方法是在啓動任務

private static void test() { 
    DBReader dbReader = new DBReader(); 
    Dictionary<string, DateTime> jobs = dbReader.getJob(); 
    JobHandler jh = new JobHandler(); 
    Stopwatch swCharge = new Stopwatch(); 
    Stopwatch swDetail = new Stopwatch(); 
    Stopwatch swHeader = new Stopwatch(); 
    //more stopwatch 

    Task[] tasks = new Task[] { 
    Task.Factory.StartNew(() => jh.processData<RawChargeCollection, RawCharge>(jobs["RawCharge"], 15, swCharge)), 
    Task.Factory.StartNew(() => jh.processData<RawDetailCollection, RawDetail>(jobs["RawDetail"], 15, swDetail)), 
    Task.Factory.StartNew(() => jh.processData<RawHeaderCollection, RawHeader>(jobs["RawHeader"], 15, swHeader)) 
    }; 
    Task.WaitAll(tasks); 
} 

的過程數據的方法

public void processData<T, S>(DateTime x, int mins, Stopwatch sw) 
      where T : List<S>, new() 
      where S : new() { 
      DateTime start = x; 
      DateTime end = x.AddMinutes(mins); 
      string fromDate, toDate; 
      StringBuilder str = new StringBuilder(); 
      XMLParser xmlParser = new XMLParser(); 
      DBWriter dbWriter = new DBWriter(); 
      while (end <= DateTime.UtcNow) { 
       fromDate = String.Format("{0:yyyy'-'MM'-'dd HH':'mm':'ss}", start); 
       toDate = String.Format("{0:yyyy'-'MM'-'dd HH':'mm':'ss}", end); 
       try { 
        sw.Restart(); 
        WebserviceClient ws = new WebserviceClient(); 

        XDocument xDoc = null; 
        var task = ws.GetRawData<S>(fromDate, toDate);  
        xDoc = XDocument.Parse(task.Result);  
        //show the download time  

        sw.Restart(); 
        T rawData = xmlParser.ParseXML<T, S>(xDoc); 

        if (rawData.Count != 0) { 
         sw.Restart(); 
         dbWriter.writeRawData<T, S>(rawData, start, end); 
         //log success 
        } 
        else { 
         //log no data 
        } 
       } 
       catch (Exception e) { 
        //log fail 
       } 
       finally { 
        start = start.AddMinutes(mins); 
        end = end.AddMinutes(mins); 
       } 
      } 
     } 

GetRawData只是負責構建在GetData中使用所需的URL。

下載數據部分:

private static Task<string> GetData(string param) { 
      string url = String.Format("my working URL/{0}", param); 
      HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); 
      request.MediaType = "application/xml"; 
      Task<WebResponse> task = Task.Factory.FromAsync(
       request.BeginGetResponse, 
       asyncResult => request.EndGetResponse(asyncResult), 
       (object)null); 

      return task.ContinueWith(t => ReadStreamFromResponse(t.Result)); 
     } 

    private static string ReadStreamFromResponse(WebResponse response) { 
     using (Stream responseStream = response.GetResponseStream()) 
     using (StreamReader sr = new StreamReader(responseStream)) { 
      //Need to return this response 
      string strContent = sr.ReadToEnd(); 
      return strContent; 
     } 
    } 

在過程數據的方法我定時從web服務下載所需要的代碼。 下載需要400ms到100000ms。正常時間在3000ms到8000ms左右。 如果我只運行1個任務,則客戶端進程時間僅比服務器進程時間稍長。

運行多個任務後然而,這需要450ms下載到3000ms(或其他)的服務器現在可能需要長達8000MS -90000ms客戶端完成下載部分。

我的方案的瓶頸應該在服務器端,從我的日誌它顯示的客戶端。

大多數爲異步編程發現的文章C#似乎演示讀取和處理流/字符串沒有XML的例子。我的代碼是否因爲XML而失敗?如果不是我的代碼有什麼問題?

編輯: 是我開發的機器和用戶/目標機器是XP,太多到.NET使用4.5或CTP。

ServicePointManager.DefaultConnectionLimit和app.config connectionManagement似乎是同樣的東西,所以我選擇了app.config,因爲它可以被更改。

起初改變最大連接幫助很大,但並沒有真正解決問題。 在使用Thread.Sleep(隨機)計時代碼塊之後,看起來「阻塞」並不涉及併發代碼。

從web服務的過程數據先下載(在這裏需要最大連接),然後做一些輕微的映射,最後寫數據庫,寫入數據庫從來沒有發生過1秒,相較於下載它是什麼,但增加最大連接後到DB(與webservice相同的數字),突然間沒有任何等待。

所以到DB最大的連接也很重要。但我不明白爲什麼用150-600ms寫入數據庫會導致等待超過20秒。

甚至讓我感到困惑的是等待時間是在下載塊中,而不是在寫入DB塊中。

+3

給出4.0標記,我假設這不是一個選項,但如果/當你可以開始使用4.5,新的HttpClient類是'本地'異步,並使異步處理的響應更容易恕我直言 - http://msdn.microsoft.com/en-us/library/system.net.http.httpclient(VS.110).aspx –

回答

1

我會回到更簡單的形式,至少對於調試,他們是每個'正常'/同步代碼。既然你會不必要地阻塞8個線程,我不會認爲這是一件大事。

我想象一下,你打的是改變限制併發請求數量的默認行爲。

從這個SO線程相關的...

Max number of concurrent HttpWebRequests

...你可能想看看什麼喬恩斯基特指出,在connectionManagement元素:

http://msdn.microsoft.com/en-us/library/fb6y0fyc.aspx

connectionManagement元素定義了連接到服務器或服務器組的最大數量的 連接。

另外,喬恩建議用Thread.Sleep替換http調用以查看併發是否受到影響非常好。如果你的8個任務都可以並行執行Thread.Sleep調用,那麼你的問題不是「頂級」併發,而是受到他們正在執行的操作(如默認併發連接限制)所強制的限制。

+2

或者使用'ServicePointManager.DefaultConnectionLimit'設置限制。 – svick