0

Task.Factory.StartNewを使用して8つのタスクを実行する私のメインプログラム

各タスクは、WebサービスからXML形式の結果を要求し、TVPを使用してMSSQLに書き込むことができるコレクションに解析されます。

プログラムは動作しますが、TPLを使用した効率の向上は私が期待したものではありません。さまざまなポイントでストップウォッチを使用した後、タスクが互いに干渉しているように見えます。おそらく、一方が他方をブロックしているようです。すべての図は、HttpWebRequestを使用するダウンロードセクションを示しています。

C#で非同期プログラミングを少し検索して読んだ後、ダウンロードセクションを非同期で実行するようにコードを変更しようとしましたが、非同期コーディングを使用しなくても、同様のレベルのブロッキングが表示されます。

私が見つけたコーディングには3つのタイプがあり、それらへの参照リンクがいくつかあります。

HttpWebRequest(.NET)を非同期で使用するにはどうすればよいですか? -このメソッドを使用する場合、ダウンロードセクションメソッドでカスタムオブジェクトを使用してXDocumentを渡します

イテレータを使用したC#での非同期プログラミング http://tomasp.net/blog/csharp-async.aspx -string / streamが返され、mainメソッドでXDocument.Load/Parseを使用して解析されます

以下のコードブロックは、私のコードで見つかって実装された最後のメソッドを示しています

タスクを開始するメインクラス

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);  
}

processDataメソッド

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;
        }
    }

processDataメソッドで、Webサービスからダウンロードするために必要なコードの時間を計りました。ダウンロードには400msから100000msかかります。通常の時間は約3000msから8000msです。1つのタスクを実行しただけの場合、クライアントの処理時間はサーバーの処理時間よりわずかに長くなります。

ただし、さらにタスクを実行した後、サーバーで450ミリ秒から3000ミリ秒(またはその他)かかるダウンロードは、クライアントがダウンロードセクションを完了するのに最大8000ミリ秒から90000ミリ秒かかる可能性があります。

私のシナリオでは、ボトルネックはサーバー側にあるはずです。私のログから、クライアントがそうであることがわかります。

非同期プログラミングC#で見つかったほとんどの記事は、XMLの例がなくても、ストリーム/文字列の読み取りと処理のデモを行っているようです。XMLが原因でコードが失敗していませんか?そうでない場合、私のコードの問題は何ですか?

編集: はい、私の開発マシンとユーザー/ターゲットマシンはXPです。.net4.5またはCTPを使用するには多すぎます。

ServicePointManager.DefaultConnectionLimitとapp.configconnectionManagementは同じもののように見えるので、変更できるのでapp.configを選択します。

最初は最大接続を変更すると大いに役立ちますが、実際には問題を解決しませんでした。Thread.Sleep(random)を使用してコードブロックのタイミングを調整した後、「ブロッキング」は並行コードに関連していないようです。

processDataは最初にWebサービスからダウンロードし(ここでは最大接続が必要)、次にマイナーマッピングを実行し、最後にDBに書き込み、DBへの書き込みに1秒以上かかることはありません。ダウンロードと比較すると、何もありませんでしたが、DBに最大接続を追加した後(同じWebサービスとしての数)突然待つことはありませんでした。

したがって、DBへの最大接続も重要です。しかし、150〜600ミリ秒でDBに書き込むと、20秒以上待機する理由がわかりません。

私を混乱させるのは、待機時間がDBブロックの書き込みではなく、ダウンロードブロックにあったことです。

4

1 に答える 1

1

少なくともデバッグのために、それぞれが「通常の」/同期コードである、より単純な形式に戻ります。最悪の場合、8つのスレッドを不必要にブロックすることになるので、まだ大したことではないと思います。

代わりに、同時リクエストの数を制限するデフォルトの動作がヒットしていると思います。

この関連するSOスレッドから...

同時HttpWebRequestsの最大数

... Jon Skeetが指し示した、connectionManagement要素を確認することをお勧めします。

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

connectionManagement要素は、サーバーまたはサーバーのグループへの接続の最大数を定義します。

また、http呼び出しをThread.Sleepだけに置き換えて、並行性が影響を受けるかどうかを確認するというJonの推奨事項は優れています。8つのタスクがすべて並列Thread.Sleep呼び出しを実行できる場合、問題は「トップレベル」の同時実行ではなく、実行内容によって強制される制限(デフォルトの同時接続制限など)です。

于 2012-06-15T09:52:47.383 に答える