0

我的主程序使用 Task.Factory.StartNew 运行 8 个任务

每个任务都会从 webservice 请求 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 服务下载所需的代码进行了计时。下载需要 400 毫秒到 100000 毫秒。正常时间在 3000ms 到 8000ms 左右。如果我只运行 1 个任务,则客户端处理时间仅比服务器处理时间稍长。

但是,在运行更多任务后,服务器上需要 450 毫秒到 3000 毫秒(或其他时间)的下载现在可能需要 8000 毫秒 -90000 毫秒,客户端才能完成下载部分。

在我的场景中,瓶颈应该在服务器端,从我的日志中它显示客户端是。

为异步编程 C# 找到的大多数文章似乎都演示了读取和处理流/字符串,没有 XML 示例。我的代码是否因为 XML 而失败?如果不是,我的代码有什么问题?

编辑: 是的,我的开发机器和用户/目标机器是 XP,使用 .net 4.5 或 CTP 太多了。

ServicePointManager.DefaultConnectionLimit 和 app.config connectionManagement 似乎是一回事,所以我选择 app.config ,因为它可以更改。

起初更改最大连接有很大帮助,但并没有真正解决问题。在使用 Thread.Sleep(random) 计时代码块之后,似乎“阻塞”与并发代码无关。

processData首先从webservice下载(这里需要最大连接),然后做一些小映射,最后写入DB,写入DB永远不会超过1秒,与下载相比什么都没有,但是在添加最大连接到DB之后(相同号码作为网络服务)突然之间没有任何等待。

所以与数据库的最大连接也很重要。但我不明白为什么用 150-600 毫秒写入数据库会导致等待超过 20 秒。

甚至让我感到困惑的是等待时间是在下载块中,而不是在写入 DB 块中。

4

1 回答 1

1

我会回到更简单的形式,至少对于调试来说,它们都是“正常”/同步代码。由于您将在最坏的情况下不必要地阻塞 8 个线程,因此我认为这不是什么大不了的事。

我想你要打的是限制并发请求数量的默认行为。

从这个相关的SO线程......

最大并发 HttpWebRequest 数

...您可能想看看 Jon Skeet 所指的 connectionManagement 元素:

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

connectionManagement 元素定义到一个服务器或一组服务器的最大连接数。

此外,Jon 建议仅用 Thread.Sleep 替换 http 调用以查看并发性是否受到影响,这一点非常棒。如果您的 8 个任务都可以执行并行 Thread.Sleep 调用,那么您的问题不是“顶级”并发,而是它们正在执行的限制(如默认并发连接限制)。

于 2012-06-15T09:52:47.383 回答