当前位置:编程学习 > C#/ASP.NET >>

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#
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,