c#线程异步问题

前端之家收集整理的这篇文章主要介绍了c#线程异步问题前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我有一些代码,当被调用调用webservice,查询数据库并从本地缓存中获取值.然后它将这三个动作的返回值组合起来以产生结果.我没有按顺序执行这些操作,而是想并行异步执行它们.这是一些虚拟/示例代码
var waitHandles = new List<WaitHandle>();

var wsResult = 0;
Func<int> callWebService = CallWebService;
var wsAsyncResult = callWebService.BeginInvoke(res => { wsResult = callWebService.EndInvoke(res); },null);
waitHandles.Add(wsAsyncResult.AsyncWaitHandle);

string dbResult = null;
Func<string> queryDB = QueryDB;
var dbAsyncResult = queryDB.BeginInvoke(res => { dbResult = queryDB.EndInvoke(res); },null);
waitHandles.Add(dbAsyncResult.AsyncWaitHandle);

var cacheResult = "";
Func<string> queryLocalCache = QueryLocalCache;
var cacheAsyncResult = queryLocalCache.BeginInvoke(res => { cacheResult = queryLocalCache.EndInvoke(res); },null);
waitHandles.Add(cacheAsyncResult.AsyncWaitHandle);

WaitHandle.WaitAll(waitHandles.ToArray());          
Console.WriteLine(string.Format(dbResult,wsResult,cacheResult));

问题是最后一行抛出一个错误,因为dbResult在执行时仍然为null.一旦调用queryDB.EndInvoke,就会发信号通知WaitHandle,并且在将queryDB.EndInvoke的结果分配给dbResult之前继续执行.这周围有一个整洁/优雅的方式吗?

注意:我应该补充说这会影响dbResult,因为queryDB是要发信号的最后一个等待句柄.

更新:虽然我接受了菲利普的答案,但是在安德烈的评论之后,我应该补充说这也有效:

var waitHandles = new List<WaitHandle>();

var wsResult = 0;
Func<int> callWebService = CallWebService;
var wsAsyncResult = callWebService.BeginInvoke(null,null);
waitHandles.Add(wsAsyncResult.AsyncWaitHandle);

string dbResult = null;
Func<string> queryDB = QueryDB;
var dbAsyncResult = queryDB.BeginInvoke(null,null);
waitHandles.Add(dbAsyncResult.AsyncWaitHandle);

var cacheResult = "";
Func<string> queryLocalCache = QueryLocalCache;
var cacheAsyncResult = queryLocalCache.BeginInvoke(null,null);
waitHandles.Add(cacheAsyncResult.AsyncWaitHandle);

WaitHandle.WaitAll(waitHandles.ToArray());

var wsResult = callWebService.EndInvoke(wsAsyncResult);
var dbResult = queryDB.EndInvoke(dbAsyncResult);
var cacheResult = queryLocalCache.EndInvoke(cacheAsyncResult);

Console.WriteLine(string.Format(dbResult,cacheResult));

解决方法

不幸的是,在EndInvoke()调用返回之前,始终会发出WaitHandle信号.这意味着你不能依赖于此.

如果你不能使用4.0,那么线程或手动等待处理系统可能会有序(或者可怕的Sleep()hack!).您还可以使用Invoked方法设置结果(因此在设置结果值后会发生EndInvoke),但这意味着将结果移动到共享位置,而不是局部变量 – 可能需要进行小的重新设计.

或者,如果你可以使用4.0,我会 – System.Threading.Tasks充满了’很棒的东西.你可以重写为:

var tasks = new List<Task>();

var wsResult = 0;
string dbResult = null;
var cacheResult = "";

tasks.Add( new Task( ()=> wsResult = CallWebService()));
tasks.Add( new Task( ()=> dbResult = QueryDB()));
tasks.Add( new Task( ()=> cacheResult = QueryLocalCache()));

tasks.ForEach( t=> t.Start());
Task.WaitAll( tasks.ToArray());

Console.WriteLine(string.Format(dbResult,cacheResult));

猜你在找的C#相关文章