代码改变世界

C# 线程手册 第三章 使用线程 手动同步

2012-02-10 12:00  DanielWise  阅读(3675)  评论(1编辑  收藏  举报

第三种同步策略关注手动同步技术,.NET Framework 提供了一个经典的技术套件。他们给了程序员使用类似WIN32线程API的底层线程API来创建和管理多线程应用程序的能力。

下面的表显示了System.Threading 命名空间中可以用于手动同步的一些类。

2012-2-10 11-20-24

ManualResetEvent 类

一个ManualResetEvent对象仅能处理signaled(true)或者non-signaled(false)两个状态。ManualResetEvent类继承自WaitHandle类,ManualResetEvent的构造函数接收一个参数用来定义对象的初始值。Set()和Reset()方法返回一个布尔值来指示已经发生的改变是否成功。

下面的代码NETThreadEvent.cs, 显示了使用non-signaled状态的ManualResetEvent类。首先我们创建一个名为mansig 的对象并赋值为false. WaitOne()方法将会等待mansig返回true或者时间超时。由于在等待的时间周期里,mansig的值没有被置成true, 所以它停止阻塞并返回false:

/*************************************
/* Copyright (c) 2012 Daniel Dong
 * 
 * Author:oDaniel Dong
 * Blog:o  www.cnblogs.com/danielWise
 * Email:o guofoo@163.com
 * 
 */

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace NETThreadEvents
{
    public class NonSignaledManual
    {
        public static void Main()
        {
            ManualResetEvent mansig;
            mansig = new ManualResetEvent(false);
            Console.WriteLine("ManualResetEvent Before WaitOne ");
            bool b = mansig.WaitOne(1000, false);
            Console.WriteLine("ManualResetEvent After WaitOne " + b);
            Console.ReadLine();
        }
    }
}

NETThreadEvents 的执行结果如下:

2012-2-10 10-28-16

在NETThreadEvents.cs中,我们构造了一个ManualResetEvent对象(构造函数传值false). 布尔值false将ManualResetEvent对象的初始状态设置为non-signaled. 然后我们调用WaitHandle基类的WaitOne()方法。WaitOne()方法接受两个参数。第一个参数是我们想让线程在WaitOne()方法处等待的时间(毫秒);由于我们将第一个参数设置为1000,所以线程在退出前等待1秒。第二个参数是exitContext. 如果你已经在上下文的同步域中且想退出同步上下文或者你想获得同步上下文,你应该设置这个参数为true.

程序在WaitOne()方法处阻塞1秒然后由于超时退出。ManualResetEvent的状态仍然是false, 因此WaitOne()函数返回false.现在我们要看一下如果我们把ManualResetEvent设置为signaled(true)会发生什么:

/*************************************
/* Copyright (c) 2012 Daniel Dong
 * 
 * Author:oDaniel Dong
 * Blog:o  www.cnblogs.com/danielWise
 * Email:o guofoo@163.com
 * 
 */

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace NETThreadEvents
{
    public class NonSignaledManual
    {
        public static void Main()
        {
            ManualResetEvent mansig;
            mansig = new ManualResetEvent(true);
            Console.WriteLine("ManualResetEvent Before WaitOne ");
            bool b = mansig.WaitOne(1000, false);
            Console.WriteLine("ManualResetEvent After WaitOne " + b);
            Console.ReadLine();
        }
    }
}

输出结果如下:

2012-2-10 10-30-04

通过将ManualResetEvent的初始状态设置为signaled, 线程不会在WaitOne()方法等待即便我们设定了1000毫秒的超时时间。在之前的例子中ManualResetEvent是non-signaled, 线程等待这个状态变成signaled, 但是在1000毫秒之后它超时了。在现在的这个例子中ManualResetEvent状态已经是non-signaled, 所以没有理由继续在WaitOne()方法上等待。为了将ManualResetEvent的状态改成non-signaled, 我们得调用ManualResetEvent的Reset()方法;如果我们想把ManualResetEvent的状态改成signaled,我们得调用Set()方法。

下面列表ManualReset.cs, 显示了使用Reset()方法;另外一个ManualSet.cs 则显示了Set()方法的使用方法:

/*************************************
/* Copyright (c) 2012 Daniel Dong
 * 
 * Author:oDaniel Dong
 * Blog:o  www.cnblogs.com/danielWise
 * Email:o guofoo@163.com
 * 
 */

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace ManualReset
{
    class Reset
    {
        [STAThread]
        static void Main()
        {
            ManualResetEvent manRE;
            manRE = new ManualResetEvent(true);
            bool state = manRE.WaitOne(1000, true);
            Console.WriteLine("ManualResetEvent After first WaitOne " + state);
            //Change the state to non-signaled
            manRE.Reset();
            state = manRE.WaitOne(5000, true);
            Console.WriteLine("ManualResetEvent After second WaitOne " + state);
            Console.ReadLine();
        }
    }
}

ManualReset 的输出结果如下:

2012-2-10 10-53-27

在ManualReset中,我们将在ManualResetEvent对象的构造函数中将其状态设置为signaled(true). 所以线程不会在第一个WaitOne()方法处等待并返回true. 然后我们将ManualResetEvent对象的状态重置为non-signaled(false), 所以我们看到线程不得不在超时退出之前等待5秒钟。

在ManualSet.cs 中我们使用Set() 方法:

/*************************************
/* Copyright (c) 2012 Daniel Dong
 * 
 * Author:oDaniel Dong
 * Blog:o  www.cnblogs.com/danielWise
 * Email:o guofoo@163.com
 * 
 */

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace ManualSet
{
    class Set
    {
        [STAThread]
        static void Main(string[] args)
        {
            ManualResetEvent manRE;
            manRE = new ManualResetEvent(false);
            Console.WriteLine("Before WaitOne");
            bool state = manRE.WaitOne(5000, true);
            Console.WriteLine("ManualResetEvent After first WaitOne " + state);
            //Change the state to signaled
            manRE.Set();
            state = manRE.WaitOne(5000, true);
            Console.WriteLine("ManualResetEvent After second WaitOne " + state);
            Console.ReadLine();
        }
    }
}

ManualSet的输出结果如下:

2012-2-10 11-00-38

在Manual Set 中,我们将ManualResetEvent对象的初始状态设置为non-signaled(false). 所以线程不得不在第一个WaitOne()方法处等待。然后我们使用Set()方法将这个状态设置为Signaled, 线程拒绝在第二个WaitOne()方法处等待然后退出。

就像WaitOne()方法会等待一个单一事件对象变成signaled一样,WaitAll()方法等待所有的事件对象变成true或者是signaled状态,或者它将等待直到发生超时,而WaitAny()方法则等待任意一个时间对象变成true或者是signaled状态。

 

下一篇介绍AutoResetEvent, Mutex 以及Interlocked…