[WF4.0 实战] AutoResetEvent详解(线程独占访问资源)

来源:互联网 发布:网络宣传平台有哪些 编辑:程序博客网 时间:2024/05/02 01:45

由来:

 

    在学习工作流的过程中,宿主程序中会出现这么一段代码:

           staticAutoResetEvent instanceUnloaded = new AutoResetEvent(false);


    然后就是在方法中这样使用:

           instanceUnloaded.Set();//将事件状态设置为终止状态,允许一个或多个等待线程继续

           instanceUnloaded.WaitOne();//对于WaitOne方法为阻止当前线程,直到收到信号!

 

对于这部分内容当时不是很理解,下面我们先介绍一下AutoResetEvent的作用,然后结合工作流分析这些代码的意图!

 

举个例子:

 

    我去书店买书,当我选中一本书后我会去收费处付钱,付好钱后再去仓库取书。这个顺序不能颠倒,我作为主线程,付钱和取书做两个辅助线程

 

代码如下:


using System;using System.Linq;using System.Activities;using System.Activities.Statements;using System.Threading; namespace CaryAREDemo{    class Me    {        const int numIterations = 550;        static AutoResetEvent myResetEvent = new AutoResetEvent(false);        static AutoResetEvent ChangeEvent = new AutoResetEvent(false);        static int number; //这是关键资源 //主线程         static void Main()        {            Thread payMoneyThread = new Thread(new ThreadStart(PayMoneyProc));            payMoneyThread.Name = "付钱线程";            Thread getBookThread = new Thread(new ThreadStart(GetBookProc));            getBookThread.Name = "取书线程";    //启动线程            payMoneyThread.Start();            getBookThread.Start();             for (int i = 1; i <= numIterations; i++)            {                Console.WriteLine("买书线程:数量{0}", i);                number = i;                //Signal that a value has been written.                //允许线程继续                myResetEvent.Set();                ChangeEvent.Set();                Thread.Sleep(0);            }            payMoneyThread.Abort();            getBookThread.Abort();        }         static void PayMoneyProc()        {            while (true)            {        //等待满足条件                myResetEvent.WaitOne();                Console.WriteLine("{0}:数量{1}", Thread.CurrentThread.Name, number);            }        }        static void GetBookProc()        {            while (true)            {//等待满足条件                ChangeEvent.WaitOne();                                            Console.WriteLine("{0}:数量{1}", Thread.CurrentThread.Name, number);                Console.WriteLine("------------------------------------------");                Thread.Sleep(0);            }        }    }}


运行结果:

         

说明:

 

    AutoResetEvent允许线程通过发信号互相通信。通常,此通信涉及线程需要独占访问的资源。

 

         1,通过将一个布尔值传递给构造函数来控制AutoResetEvent的初始状态,如果初始状态为终止状态,则为 true即该线程阻塞,并等待当前控制资源的线程;否则为 false。

         2,线程也可以通过调用AutoResetEvent 上的 WaitOne 来等待信号。

         3,通过调用 Set发出资源可用的信号,以释放等待线程。

 

    通俗的来讲只有等myResetEven.Set()成功运行后,myResetEven.WaitOne()才能够获得运行机会;Set是发信号,WaitOne是等待信号,只有发了信号,等待的才会执行。如果不发的话,WaitOne后面的程序就永远不会执行。

 

 

结合工作流的宿主程序:

 

创建工作流:

 

static AutoResetEvent instanceUnloaded = new AutoResetEvent(false); public static Guid CreateAndRun(RequestInfo.RequestInfo Request)        {            SqlWorkflowInstanceStore instanceStore = new SqlWorkflowInstanceStore("server=.;database=aspnetdb;uid=sa;pwd=123456");            InstanceView view = instanceStore.Execute(instanceStore.CreateInstanceHandle(), new CreateWorkflowOwnerCommand(), TimeSpan.FromSeconds(30));            instanceStore.DefaultInstanceOwner = view.InstanceOwner;            IDictionary<string, object> input = new Dictionary<string, object>             {                { "Request" , Request }            };            WorkflowApplication application = new WorkflowApplication(new ApplyBlogFlow(), input);            application.InstanceStore = instanceStore;            //获取或设置当前工作流实例处于空闲状态并可被保留时调用的 ActivityFunc            application.PersistableIdle = (e) =>            {                instanceUnloaded.Set();                return PersistableIdleAction.Unload;            };            //获取或设置卸载当前工作流            application.Unloaded = (e) =>            {                instanceUnloaded.Set();            };            //获取或设置当前工作流实例遇到未处理的异常时            application.OnUnhandledException = (ex) =>            {                Console.Write("Exception");                return UnhandledExceptionAction.Terminate;            };            Guid id = application.Id;            application.Run();            //阻止当前线程,直到当前waithand收到信号            instanceUnloaded.WaitOne();            return id;        }

    仔细看这段代码,在初始时将AutoResetEvent(false),即非终止状态,但是当application.Run();之后就WaitOne,说明此时将AutoResetEvent设置成了终止状态,即阻塞当前线程。为什么要这样呢?目的很简单,因为这个执行过程为异步,如果当调用启动工作流的方法后,不去阻止当前方法的执行,那么这个方法就这样执行结果,返回我们创建成功,可是问题就是,这个时候很可能工作流还没有执行完成,而结果却先告诉我们了,这样显然不合理。

 

如果大家对于上述的解释还不理解,可以参考下调用流程:

 

         CreateAndRun开始---WaitOne---》当前线程被阻止,即不再往下执行---》真正启动工作流---》工作流执行完等待流后会被设置为空闲状态---PersistableIdle---instanceUnloaded.Set();---AutoResetEvent可以继续--- return id;---》整个方法执行完成

 

总结:

 

          AutoResetEvent实现同步,只是众多方法中之一,记得当时在考试系统的教师判分处应用了锁,当时使用的是数据库锁,保证某个表同一时间只能有一个用户在操作,这也是一个实现同步的方式,而且我们在学习java时也知道synchronized实现方法同步等方式。无论是数据库锁,synchronized,还是AutoResetEvent的方式只是它们的侧重不同,针对的对象不同,但是目的都是在控制操作的同步进行。


         其实对于多线程的同步问题就是在程序的执行时多了一步验证(一个门一次只能通过一个即n=1的情况),是可以执行,还是需要等待而已,对于不同的同步技术只是对不同的对象设置验证,如数据库锁是对表的操作进行限制。


    通过整理发现,其实AutoResetEvent只是实现同步的一种方式,并没有当时想的那么深奥,关键还是在于对于不懂的知识的学习与整理,了解到它是解决同步的方式后,再结合我们之前使用过的一些同步方法,这些知识就又简单了,所以我们面对学也就开心了!


 


  
1 0
原创粉丝点击