我的主程序使用Task.Factory.StartNew运行8个任务
每个任务将请求来自webservice的XML格式结果,然后解析为集合,可以使用TVP写入MSSQL。
该程序工作,但效率提高使用TPL不是我所期望的。在不同的地方使用秒表后,在我看来,这些任务似乎是互相干扰的,也许是一个阻塞另一个。所有的数字指向使用HttpWebRequest的下载部分。
在对c#中的异步编程进行搜索和阅读后,我尝试修改代码以异步运行下载部分,但结果仍然显示出类似的阻塞级别,而不使用异步编码。
我发现了3种类型的编码以及一些指向它们的参考链接:
How to use HttpWebRequest (.NET) asynchronously? -When我使用此方法使用下载部分方法中使用自定义对象传递XDocument
在C#中使用迭代器返回异步编程,返回字符串/流,并在主方法中使用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方法中,我计时了从webservice下载所需的代码。下载时间从400 to到100000ms不等。正常时间大约3000到8000毫秒。如果我只运行一个任务,客户端进程时间仅略长于服务器进程时间。
但是,在运行了更多的任务之后,服务器上需要450 to到3000 to (或其他任何东西)的下载现在可以占用8000 to 900 to,客户机才能完成下载部分。
在我的场景中,瓶颈应该在服务器端,从我的日志中可以看出客户机是。
大多数关于异步编程C#的文章似乎都演示了读取和处理流/字符串,而没有使用XML的示例。我的代码因为XML而失败吗??如果不是,我的代码有什么问题?
编辑:是的,我的开发机器和用户/目标机器是XP,太多了,无法使用.net 4.5或.net。
ServicePointManager.DefaultConnectionLimit和app.config connectionManagement似乎是一回事,所以我选择app.config,因为这是可以改变的。
起初,改变最大连接很有帮助,但并没有真正解决问题。在使用Thread.Sleep(随机)的定时代码块之后,“阻塞”似乎与并发代码无关。
processData首先从webservice下载(这里需要最大连接),然后执行一些小的映射,最后写到DB,向DB的写入永远不会超过1秒,与下载相比,它没有什么意义,但是在添加到DB的最大连接(与webservice相同的号码)之后,没有任何突然的等待。
因此,与DB的最大连接也很重要。但是我不明白为什么用150-600ms写到DB会导致超过20秒的等待。
甚至让我感到困惑的是等待时间是在下载块中,而不是在写DB块中。
发布于 2012-06-15 09:52:47
我会回到更简单的形式,至少在调试时,它们都是“正常”/同步代码。由于您将是最坏的情况下阻塞8个线程不必要,我不会认为这是一个大问题。
我可以想象,您所触及的是限制并发请求数量的默认行为。
从这个相关的线索..。
Max number of concurrent HttpWebRequests
...you可能想看看Jon所指的connectionManagement元素:
http://msdn.microsoft.com/en-us/library/fb6y0fyc.aspx
connectionManagement元素定义到服务器或服务器组的最大连接数。
此外,Jon建议用Thread.Sleep替换http调用,以查看并发性是否受到影响,这是很好的。如果您的8个任务都可以执行并行Thread.Sleep调用,那么您的问题不是“顶级”并发,而是由它们所执行的操作(比如默认的并发连接限制)强制执行的限制。
https://stackoverflow.com/questions/11048087
复制相似问题