我有一个项目,我需要多个项目来列出项目列表。对于每个项目,我都会致电API,以获取此信息。循环起作用,尽管需要4到5分钟才能完成(很多)。
用来像这样的代码:
foreach (var project in projects)
{
string url = urlOneProject + project.name + secondPartUrl + "?authtoken=" + authToken;
HttpWebRequest request;
request = (HttpWebRequest)WebRequest.Create(url);
request.Accept = "application/json";
request.ContentType = "application/json";
var executions = new Execs();
var response = (HttpWebResponse)(await request.GetResponseAsync());
using (response)
{
using (var reader = new StreamReader(response.GetResponseStream()))
{
JavaScriptSerializer js = new JavaScriptSerializer();
var objText = reader.ReadToEnd();
executions = (Execs)js.Deserialize(objText, typeof(Execs));
}
}
execs.AddRange(executions.executions);
}
为了提高性能,我认为使用线程可能是个好主意。所以,我想出了这样的事情:
ManualResetEvent resetEvent = new ManualResetEvent(false);
int toProcess = projects.Count;
foreach (var project in projects)
{
new Thread(() =>
{
string url = urlOneProject + project.name + secondPartUrl + "?authtoken=" + authToken;
HttpWebRequest request;
request = (HttpWebRequest)WebRequest.Create(url);
request.Accept = "application/json";
request.ContentType = "application/json";
var executions = new Execs();
var response = (HttpWebResponse)(await request.GetResponseAsync());
using (response)
{
using (var reader = new StreamReader(response.GetResponseStream()))
{
JavaScriptSerializer js = new JavaScriptSerializer();
var objText = reader.ReadToEnd();
executions = (Execs)js.Deserialize(objText, typeof(Execs));
}
}
lock (execs)
{
execs.AddRange(executions.executions);
}
if (Interlocked.Decrement(ref toProcess) == 0)
resetEvent.Set();
}).Start();
}
此代码的问题是行:
var response = (HttpWebResponse)(await request.GetResponseAsync());
从我添加Thread
的那一刻起不再编译。我得到的错误是
"等待'运算符只能在异步lambda表达式中使用"
当我不使用线程时,这不是问题。GetResponseAsync
是async
功能,await
的使用是强制性的。我尝试删除它(我同意这不是逻辑上的,但我没有选项),但是编译器告诉我,我需要await
的async
功能。我不太了解Thread
的实现发生了什么变化。
我不是正确地使用线程吗?我该怎么做才能纠正或实施我想正确执行的操作?
您正在混合编码的多个范式,viz async / await
和Oldschool线程启动和同步,这可能会导致麻烦。
根据上述评论
- 您的代码未编译的原因是,您正在尝试在传递给线程的同步代码中使用
await
。您也可以将Lambdas作为async
。 -
Task
比Thread
更安全,TPL提供了丰富而表达的工具来协助异步和并行性。 - 如果您孤立地处理每个并行任务,而无需共享任何数据(例如您要锁定的集合),而是从每个任务中返回所得数据,则可以使用LINQ将结果整理到线程安全中方式。
var myTasks = projects.Select(async project =>
{
var url = $"urlOneProject{project.name}{secondPartUrl}?authtoken={authToken}";
var request = (HttpWebRequest) WebRequest.Create(url);
request.Accept = "application/json";
request.ContentType = "application/json";
using (var response = (HttpWebResponse) (await request.GetResponseAsync()))
using (var reader = new StreamReader(response.GetResponseStream()))
{
var objText = await reader.ReadToEndAsync();
return JsonConvert.DeserializeObject<Execs>(objText);
}
});
var execs = (await Task.WhenAll(myTasks))
.SelectMany(result => result.executions);
其他注释
- 不要使用
JavaScriptSerializer
-即使是MSDN Docco都说使用Newtonsoft JSON - 我包括的
reader.ReadToEndAsync
有一个异步版本。 - 您可以放下锁和
ManualResetEvent
-由于每个任务返回结果,我们将其保留到Task.WhenAll
来整理数据。 - 您可以用
SelectMany
将多个 - 相邻的
using
条款是可堆叠的 - 它可以节省一些眼睛的凹痕。
executions
的孩子弄平