C# 使用async/await时使用未分配的局部变量
此代码(不带C# 使用async/await时使用未分配的局部变量,c#,async-await,C#,Async Await,此代码(不带wait)编译: IEnumerable<PingResponse> pingResponses; using (var prestoWcf = new PrestoWcf<IPingService>()) { pingResponses = prestoWcf.Service.GetAllForPingRequest(this.PingRequest); } foreach (PingResponse response in pingRespons
wait
)编译:
IEnumerable<PingResponse> pingResponses;
using (var prestoWcf = new PrestoWcf<IPingService>())
{
pingResponses = prestoWcf.Service.GetAllForPingRequest(this.PingRequest);
}
foreach (PingResponse response in pingResponses) { // code here }
错误是:使用未分配的局部变量“pingResponses”
为什么引入async/await会导致此问题?使用允许您返回对象的
StartNew
重载:
IEnumerable pingResponses=
等待任务。工厂。开始新建(()=>
{
使用(var prestoWcf=new prestoWcf())
{
返回prestoWcf.Service.GetAllForPingRequest(this.PingRequest);
}
});
它不起作用的原因是编译器无法知道提供给StartNew
方法的委托总是在执行foreach
循环之前执行。你知道,我也知道,但编译器无法证明它,因为它目前有明确的可赋值规则
虽然有几种“变通方法”可以诱使编译器允许您这样做,但最好也是最惯用的解决方案是让任务返回一个结果,而不是变异一个闭合变量。这样一来,你就不再依赖于任务的副作用,而是依赖于结果本身。这使得代码更容易推理(任务和使用它的代码可以分别分析,而不是让每个任务的实现相互依赖),并确保不同线程之间共享的内存的正确同步(一个非常重要的任务)
关于实际代码,已在以下文件中提供:
IEnumerable pingResponses=
等待任务。工厂。开始新建(()=>
{
使用(var prestoWcf=new prestoWcf())
{
返回prestoWcf.Service.GetAllForPingRequest(this.PingRequest);
}
});
更好的是,根据,您可以使用一个适当异步的方法,而不是在线程池线程中使用同步方法。这样做可以让您利用操作系统的功能,在网络请求完成时通知您,而不会浪费线程池的资源,因为线程池在那里什么都不做。您应该发送一个异步网络请求,而不是在后台线程上发送同步请求。这样就不会浪费线程。您是否有
GetAllForPingRequestsAsync
?看到该类的名称中包含WCF,WCF代理生成工具应该能够自动生成一个代理,该代理返回任务
(这就是您将如何实现SLaks的建议)@ScottChamberlain我还没有GetAllForPingRequestsAsync
。看来我需要这么做。谢谢您的提示。@BobHorn如果您使用svcutil.exe
生成代理类,请不要添加/async
、/a
或/syncOnly
开关,因为这将覆盖默认值,而默认值会使基于任务的异步版本生效。如果您使用的是GUI,请确保选中该框,如果任务的单选按钮需要以较新版本的.NET为目标,或者使用svcuti
生成代理。我会做两个小更改:1。使用Task.Run()
而不是StartNew()
,它比较短,并且总是在线程池上执行工作。2.在这里使用var
,我认为这里不需要完整类型,但是编译器在原始代码中需要它。
IEnumerable<PingResponse> pingResponses;
await Task.Factory.StartNew(() =>
{
using (var prestoWcf = new PrestoWcf<IPingService>())
{
pingResponses = prestoWcf.Service.GetAllForPingRequest(this.PingRequest);
}
});
foreach (PingResponse response in pingResponses) { // code here }
IEnumerable<PingResponse> pingResponses =
await Task.Factory.StartNew(() =>
{
using (var prestoWcf = new PrestoWcf<IPingService>())
{
return prestoWcf.Service.GetAllForPingRequest(this.PingRequest);
}
});
IEnumerable<PingResponse> pingResponses =
await Task.Factory.StartNew(() =>
{
using (var prestoWcf = new PrestoWcf<IPingService>())
{
return prestoWcf.Service.GetAllForPingRequest(this.PingRequest);
}
});