C#中的同步等待
第一次在CSDN上发帖,水平有限,有些忐忑,希望这篇文章对大家能够有些帮助吧以下文章转自
http://www.soft-bin.com/html/2010/07/13/syncwaitforcsharp/
我们在使用有些异步调用的时候,可能会有顺序执行某一个流程的要求,也就是说,如果能把异步调用转换成同步调用就好了。通常情况下,我们使用更改标志或者事件,锁等同步手段来实现这个操作,以下是相关的代码:
// 我的逻辑函数,可能是一系列同步调用的组合
public void MainLogic()
{
// 一个异步调用,我需要等它完成
this.flag = false;
AsyncObj.AsyncFuncCompleted += this.OnAsyncFuncCompleted;
AsyncObj.AsyncFunc();
// 好了,开始等吧
while(this.flag == false)
{
// 不要着急,休息,休息一会儿
Thread.Sleep(500);
}
}
private void OnAsyncFuncCompleted()
{
this.flag = true;
}
或许我们不用flag,而是改成一个Event,Mutex或其他的什么东西,在异步事件完成的时候,将Event或Mutex激活一下,这些是大同小异的了。
但这么做的弊端是显而易见的:
1: 不管是while循环等待,还是等待Event信号,我的程序肯定是死在异步调用那里了。如果这个调用的执行过程很长,而且这段代码是运行在UI线程上的话,那么UI肯定就死住了,这不是我们希望看到的结果。
2: 大多数情况下,这种代码会产生死锁。即便不死锁,存在的隐患也是不可接受的。试想,如果异步操作完成事件,是被切换到主线程上来触发的,说简单点就是完成事件并非直接从其他线程回调,而是以消息的形式抛回调用线程(实际上很多SDK就是这么干的)的话,结果可想而知,我们的线程正忙着跟while循环纠结着呢,哪有时间去理会新抛过来的消息?
所以实际情况中,除非你非常清楚这其中的厉害关系以及你调用的函数的实现机理,并且能够保证这些代码以后还是由你来维护,或者至少要找个明白人而不是糊涂蛋来维护,否则你就不要干这些傻事,这几乎是一个道德问题了。
难道没有好的办法实现异步转同步了么? 当然有,不然我写这篇文章就有点令人生厌了。上面的等待方法里,再加一个消息循环就可以了,有了消息循环,我们就能规避上面提到的两个弊端。
这个时候有人要问了,如果线程是工作线程怎么办,工作线程没有消息队列的。Okay,MSDN里对消息队列的解释是,消息队列使用了类似copy-on-write的技术,就是你需要它时,它就会存在,不存在也给你现场创建一个出来。所以这方面不用担心。
以下是我的同步等待方法,功能还算比较强大。
查看源代码打印帮助using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Reflection;
namespace Luckzj
{
[StructLayout(LayoutKind.Sequential)]
public struct MSG
{
public int hwnd;
public int message;
public int wParam;
public int lParam;
public int time;
public POINTAPI pt;
}
// 构建消息循环需要的东西
static class WinApi
{
[DllImport("user32 ", EntryPoint = "PeekMessage")]
public static extern int PeekMessage(
out MSG lpMsg,
int hwnd,
int wMsgFilterMin,
int wMsgFilterMax,
int wRemoveMsg
);
[DllImport("user32 ", EntryPoint = "DispatchMessage")]
public static extern int DispatchMessage(
ref MSG lpMsg
);
[DllImport("user32 ", EntryPoint = "TranslateMessage")]
public static extern int TranslateMessage(
ref MSG lpMsg
);
}
[StructLayout(LayoutKind.Sequential)]
public struct POINTAPI
{
public int x;
public int y;
}
public class SyncWaitor
{
// 同步等待,等待ValidFunc的返回值为bool
public static void Wait(Delegate ValidFunc, params object[] param)
{
// check delegate
MethodInfo mi = ValidFunc.Method;
if (mi.ReturnType != typeof(bool))
{
// 这个错误通常情况下在测试期间就会被发现
throw new Exception("Valid function must have a return type of bool.");
}
// 等着吧
while (true)
{
// 如果 ValidFunc 验证成功了,那说明我们该返回了
bool ret = (bool)ValidFunc.DynamicInvoke(param);
if (ret == true)
{
break;
}
// 执行一条消息
MSG msg;
// 这里只能用PeekMessage,否则GetMessage阻塞在这里可能导致死锁
int iRet = WinApi.PeekMessage(out msg, 0, 0, 0, 1);
// 返回大于0表示收到了消息
if (iRet > 0)
{
WinApi.TranslateMessage(ref msg);
WinApi.DispatchMessage(ref msg);
}
}
}
}
}
SyncWaitor 的使用方法很简单,只需要在等待的时候调用
SyncWaitor.Wait(Delegate, params object[]);
就可以了,当然,你首先得有一个验证等待是否完成的函数。我们把文章前面提到的等待改版一下:
// 随便定义一个新的代理
public delegate void Delegate0();
// 我的逻辑函数,可能是一系列同步调用的组合
public void MainLogic()
{
// 一个异步调用,我需要等它完成
this.flag = false; // 我们仍然需要这个标志
AsyncObj.AsyncFuncCompleted += this.OnAsyncFuncCompleted;
AsyncObj.AsyncFunc();
SyncWaitor.Wait(new Delegate0(ValidAsyncComplete));
}
private void OnAsyncFuncCompleted()
{
this.flag = true;
}
private bool ValidAsyncComplete()
{
return this.flag;
}
为了使用方便,我们还可以在SyncWaitor里增加一个Wait的重载版本:
public static void Wait(ref bool flag);
--------------------编程问答-------------------- 谢谢楼主,学习了 --------------------编程问答-------------------- 瞄一眼飘过~ --------------------编程问答-------------------- 奇怪,之前的回复怎么不见了?
补充:.NET技术 , C#