第一次在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);
以下文章转自
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);
解决方案 »
- 关于Sql中的between的问题,求解答
- winform,datagridview中选定行的数据转移到另一个窗体的datagridview
- 求C#屏蔽键盘的部分功能键的代码
- 制作进度条问题
- C#如何在窗体关闭的时候立即关闭线程?
- 继承Form后的奇怪问题
- 图片怎么保留修改痕迹:如果要在一幅图的中间画一条线,用代码怎么来实现
- 能否自行更改ComboBox组件的高度呢?(WinForm)
- c# 能否固定form的高度,比如说1012高,怎么拉滚动条最大就到1012!
- c#中有没有开n次方的函数???
- 求高手。指教一下怎样模拟QQ那样的截图功能
- 简体中文.net开发平台在简体中文操作系统开发而在繁体操作系统安装出现乱码问题
有必要这么大费周章么?
这个是不一样的,呵呵
WaitHandle 在等待的时候会阻塞当前线程,导致界面死掉。这个类使用的等待方式就是我文章里摒弃掉的那种。你可以试下这段代码:
public ManualResetEvent evt = new ManualResetEvent(false);// 一个按钮的点击事件
private void button1_Click(object sender, EventArgs e)
{
evt.Reset();
ThreadPool.QueueUserWorkItem(new WaitCallback(DoTask), evt);
WaitHandle.WaitAny(new WaitHandle[] { evt });
}void DoTask(object obj)
{
Thread.Sleep(5000);
ManualResetEvent evt = (ManualResetEvent)obj;
evt.Set();
}
上面这段代码,点击按钮,则界面会卡死,下面这段代码则不会:
public bool flag = false;// 一个按钮的点击事件
private void button1_Click(object sender, EventArgs e)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(DoTask));
SyncWaitor.Wait(new Delegate0(Valid));
}void DoTask(object obj)
{
Thread.Sleep(5000);
flag = true;
}bool Valid()
{
return flag;
}