第一次在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); 

解决方案 »

  1.   

    你这个我怎么感觉就是自己手动写了个WaitHandle出来?
    有必要这么大费周章么?
      

  2.   


    这个是不一样的,呵呵
    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;
    }