我有一个循环浏览应用列表的程序。
Apps
--------
App1
App2
App3
现在,对于每个人,我都会做一个http请求,以获取每个应用程序的构建列表作为Xml。
这样的请求,
http://example.com/getapplist.do?appid=App1
给了我一个回复,
<appid name="App1">
<buildid BldName="Bld3" Status="Not Ready"></buildid>
<buildid BldName="Bld2" Status="Ready"></buildid>
<buildid BldName="Bld1" Status="Ready"></buildid>
</appid>
现在,我使用状态&#34;准备好&#34; 获得最高版本号,然后执行另一个网络API呼叫,例如,
http://example.com/getapplist.do?appid=App1&bldid=Bld2
这给了我一个回复,
<buildinfo appid="App1" buildid="Bld2" value="someinfo"></build>
我将它们提供给内部数据表。但是现在,这个程序需要花费很长时间才能完成(3个小时),因为我有接近2000个appids,每个id有2个Web请求。我尝试使用指定here的 BackgroundWorker 对此问题进行排序。我想到将来自http响应的所有信息整理成单个XML,然后使用该XML进行进一步处理。这会引发错误,
另一个进程正在使用的文件
所以我的代码看起来像,
if (!backgroundWorker1.IsBusy)
{
for(int i = 0; i < appList.Count; i++)
{
BackgroundWorker bgw = new BackgroundWorker();
bgw.WorkerReportsProgress = true;
bgw.WorkerSupportsCancellation = true;
bgw.DoWork += new DoWorkEventHandler(bgw_DoWork);
bgw.ProgressChanged += new ProgressChangedEventHandler(bgw_ProgressChanged);
bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgw_RunWorkerCompleted);
//Start The Worker
bgw.RunWorkerAsync();
}
}
DoWork
函数选择标记值并将其放入XML中。
我可以从所有后台工作人员的所有http响应中将app-buildinfo详细信息放入公共文件中的最佳方法是什么?
答案 0 :(得分:4)
HTTP请求本质上是IO绑定和异步的,没有理由使用后台工作程序来完成您的需要。
您可以通过Microsoft.Bcl.Async
和HttpClient
利用与.NET 4兼容的async-await
:
private async Task ProcessAppsAsync(List<string> appList)
{
var httpClient = new HttpClient();
// This will execute your IO requests concurrently,
// no need for extra threads.
var appListTasks = appList.Select(app => httpClient.GetAsync(app.Url)).ToList();
// Wait asynchronously for all of them to finish
await Task.WhenAll(appListTasks);
// process each Task.Result and aggregate them to an xml
using (var streamWriter = new StreamWriter(@"PathToFile")
{
foreach (var appList in appListTasks)
{
await streamWriter.WriteAsync(appList.Result);
}
}
}
这样,您可以同时处理所有请求,并在完成后处理所有请求。
答案 1 :(得分:0)
此解决方案适用于.Net 2.0及更高版本,使用WebClient类中的异步方法,并使用递减Interlocked类和普通lock的计数器来序列化将结果写入文件。
var writer = XmlWriter.Create(
new FileStream("api.xml",
FileMode.Create));
writer.WriteStartElement("apps"); // root element in the xml
// lock for one write
object writeLock = new object();
// this many calls
int counter = appList.Count;
foreach (var app in appList)
{
var wc = new WebClient();
var url = String.Format(
"http://example.com/getapplist.do?appid={0}&bldid=Bld2",
app);
wc.DownloadDataCompleted += (o, args) =>
{
try
{
var xd = new XmlDocument();
xd.LoadXml(Encoding.UTF8.GetString(args.Result));
lock (writeLock)
{
xd.WriteContentTo(writer);
}
}
finally
{
// count down our counter in a thread safe manner
if (Interlocked.Decrement(ref counter) == 0)
{
// this was the last one, close nicely
writer.WriteEndElement();
writer.Close();
((IDisposable) writer).Dispose();
}
}
};
wc.DownloadDataAsync(
new Uri(url));
}