我的主程序使用 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 块中。