概述
当一个线程开始一个活动(此活动必须完成后,其他线程才能开始)时,它调用 Reset 以将 ManualResetEvent 置于非终止状态。 此线程可被视为控制 ManualResetEvent。 调用 ManualResetEvent 上的 WaitOne 的线程将阻止,并等待信号。 当控制线程完成活动时,它调用Set 以发出等待线程可以继续进行的信号。 并释放所有等待线程。
一旦它被终止,ManualResetEvent 将保持终止状态,直到它被手动重置。 即对 WaitOne 的调用将立即返回。
可以通过将布尔值传递给构造函数来控制 ManualResetEvent 的初始状态,如果初始状态处于终止状态,为 true;否则为 false。
ManualResetEvent 也可以同 staticWaitAll 和 WaitAny 方法一起使用。
生活中的例子
AutoResetEvent :在上海坐地铁,检票口有个刷卡的通道,一次只能一个人刷卡后通过,而我过后,它又是关闭的,另一个人又得再刷卡.一次操作,只有一个事件,这时就是非终止状态,一般是用来同步访问资源.
ManualResetEvent :公司园区的大门很大,一次可以多人通过。
ManualResetEvent和AutoResetEvent 比较
ManualResetEvent和AutoResetEvent都继承自EventWaitHandler,它们的唯一区别就在于父类 EventWaitHandler的构造函数参数EventResetMode不同,这样我们只要弄清了参数EventResetMode值不同时,EventWaitHandler类控制线程同步的行为有什么不同,两个子类也就清楚了。
共同点:
A、Set方法将事件状态设置为终止状态,允许一个或多个等待线程继续;Reset方法将事件状态设置为非终止状态,导致线程阻止;WaitOne阻止当前线程,直到当前线程的WaitHandler收到事件信号。
B、可以通过构造函数的参数值来决定其初始状态,若为true则事件为终止状态从而使线程为非阻塞状态,为false则线程为阻塞状态。
C、如果某个线程调用WaitOne方法,则当事件状态为终止状态时,该线程会得到信号,继续向下执行。
不同点:
A、AutoResetEvent.WaitOne()每次只允许一个线程进入,当某个线程得到信号后,AutoResetEvent会自动又将信号置为不发送状态,则其他调用WaitOne的线程只有继续等待,也就是说AutoResetEvent一次只唤醒一个线程;
B、ManualResetEvent则可以唤醒多个线程,因为当某个线程调用了ManualResetEvent.Set()方法后,其他调用WaitOne的线程获得信号得以继续执行,而ManualResetEvent不会自动将信号置为不发送。
C、也就是说,除非手工调用了ManualResetEvent.Reset()方法,则ManualResetEvent将一直保持有信号状态,ManualResetEvent也就可以同时唤醒多个线程继续执行。
再来看下EventWaitHandle的代码
再来看下ManualResetEvent的代码
其实这两个的差别就是AutoResetEvent是base(initialState,EventResetMode.AutoReset)而ManualResetEvent是base(initialState,EventResetMode.ManualReset).
AutoResetEvent是操作单个线程的,而ManualResetEvent可以操作多个线程.
AutoResetEvent开发示例
在主线程运行后,新开一个新线程,由新线程来控制主线程的等待和执行.
类关系图
运行结果
AutoResetEvent 允许线程通过发信号互相通信。 通常,当线程需要独占访问资源时使用该类。
线程通过调用 AutoResetEvent 上的 WaitOne 来等待信号。 如果 AutoResetEvent 为非终止状态,则线程会被阻止,并等待当前控制资源的线程通过调用 Set 来通知资源可用。
调用 Set 向 AutoResetEvent 发信号以释放等待线程。 AutoResetEvent 将保持终止状态,直到一个正在等待的线程被释放,然后自动返回非终止状态。 如果没有任何线程在等待,则状态将无限期地保持为终止状态。
如果当 AutoResetEvent 为终止状态时线程调用 WaitOne,则线程不会被阻止。 AutoResetEvent 将立即释放线程并返回到非终止状态。
ManualResetEvent 允许线程通过发信号互相通信。 通常,此通信涉及一个线程在其他线程进行之前必须完成的任务。当一个线程开始一个活动(此活动必须完成后,其他线程才能开始)时,它调用 Reset 以将 ManualResetEvent 置于非终止状态。 此线程可被视为控制 ManualResetEvent。 调用 ManualResetEvent 上的 WaitOne 的线程将阻止,并等待信号。 当控制线程完成活动时,它调用Set 以发出等待线程可以继续进行的信号。 并释放所有等待线程。
一旦它被终止,ManualResetEvent 将保持终止状态,直到它被手动重置。 即对 WaitOne 的调用将立即返回。
可以通过将布尔值传递给构造函数来控制 ManualResetEvent 的初始状态,如果初始状态处于终止状态,为 true;否则为 false。
ManualResetEvent 也可以同 staticWaitAll 和 WaitAny 方法一起使用。
生活中的例子
AutoResetEvent :在上海坐地铁,检票口有个刷卡的通道,一次只能一个人刷卡后通过,而我过后,它又是关闭的,另一个人又得再刷卡.一次操作,只有一个事件,这时就是非终止状态,一般是用来同步访问资源.
ManualResetEvent :公司园区的大门很大,一次可以多人通过。
ManualResetEvent和AutoResetEvent 比较
ManualResetEvent和AutoResetEvent都继承自EventWaitHandler,它们的唯一区别就在于父类 EventWaitHandler的构造函数参数EventResetMode不同,这样我们只要弄清了参数EventResetMode值不同时,EventWaitHandler类控制线程同步的行为有什么不同,两个子类也就清楚了。
共同点:
A、Set方法将事件状态设置为终止状态,允许一个或多个等待线程继续;Reset方法将事件状态设置为非终止状态,导致线程阻止;WaitOne阻止当前线程,直到当前线程的WaitHandler收到事件信号。
B、可以通过构造函数的参数值来决定其初始状态,若为true则事件为终止状态从而使线程为非阻塞状态,为false则线程为阻塞状态。
C、如果某个线程调用WaitOne方法,则当事件状态为终止状态时,该线程会得到信号,继续向下执行。
不同点:
A、AutoResetEvent.WaitOne()每次只允许一个线程进入,当某个线程得到信号后,AutoResetEvent会自动又将信号置为不发送状态,则其他调用WaitOne的线程只有继续等待,也就是说AutoResetEvent一次只唤醒一个线程;
B、ManualResetEvent则可以唤醒多个线程,因为当某个线程调用了ManualResetEvent.Set()方法后,其他调用WaitOne的线程获得信号得以继续执行,而ManualResetEvent不会自动将信号置为不发送。
C、也就是说,除非手工调用了ManualResetEvent.Reset()方法,则ManualResetEvent将一直保持有信号状态,ManualResetEvent也就可以同时唤醒多个线程继续执行。
先看下AutoResetEvent 的代码
namespace System.Threading {
using System;
using System.Security.Permissions;
using System.Runtime.InteropServices;
[HostProtection(Synchronization=true, ExternalThreading=true)]
[System.Runtime.InteropServices.ComVisible(true)]
public sealed class AutoResetEvent : EventWaitHandle
{
public AutoResetEvent(bool initialState) : base(initialState,EventResetMode.AutoReset){ }
}
}
再来看下EventWaitHandle的代码
namespace System.Threading
{
using System;
using System.Threading;
using System.Runtime.CompilerServices;
using System.Security.Permissions;
using System.IO;
using Microsoft.Win32;
using Microsoft.Win32.SafeHandles;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
#if !FEATURE_PAL
using System.Security.AccessControl;
#endif
[HostProtection(Synchronization=true, ExternalThreading=true)]
[ComVisibleAttribute(true)]
public class EventWaitHandle : WaitHandle
{
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
public EventWaitHandle(bool initialState, EventResetMode mode) : this(initialState,mode,null) { }
[SecurityPermissionAttribute(SecurityAction.LinkDemand,Flags=SecurityPermissionFlag.UnmanagedCode)]
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
public EventWaitHandle(bool initialState, EventResetMode mode, string name)
{
if(null != name && System.IO.Path.MAX_PATH < name.Length)
{
throw new ArgumentException(Environment.GetResourceString("Argument_WaitHandleNameTooLong",name));
}
SafeWaitHandle _handle = null;
switch(mode)
{
case EventResetMode.ManualReset:
_handle = Win32Native.CreateEvent(null, true, initialState, name);
break;
case EventResetMode.AutoReset:
_handle = Win32Native.CreateEvent(null, false, initialState, name);
break;
default:
throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag",name));
};
if (_handle.IsInvalid)
{
int errorCode = Marshal.GetLastWin32Error();
_handle.SetHandleAsInvalid();
if(null != name && 0 != name.Length && Win32Native.ERROR_INVALID_HANDLE == errorCode)
throw new WaitHandleCannotBeOpenedException(Environment.GetResourceString("Threading.WaitHandleCannotBeOpenedException_InvalidHandle",name));
__Error.WinIOError(errorCode, "");
}
SetHandleInternal(_handle);
}
[SecurityPermissionAttribute(SecurityAction.LinkDemand,Flags=SecurityPermissionFlag.UnmanagedCode)]
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
public EventWaitHandle(bool initialState, EventResetMode mode, string name, out bool createdNew)
#if !FEATURE_PAL
: this(initialState, mode, name, out createdNew, null)
{
}
[SecurityPermissionAttribute(SecurityAction.LinkDemand,Flags=SecurityPermissionFlag.UnmanagedCode)]
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
public unsafe EventWaitHandle(bool initialState, EventResetMode mode, string name, out bool createdNew, EventWaitHandleSecurity eventSecurity)
#endif
{
if(null != name && System.IO.Path.MAX_PATH < name.Length)
{
throw new ArgumentException(Environment.GetResourceString("Argument_WaitHandleNameTooLong",name));
}
Win32Native.SECURITY_ATTRIBUTES secAttrs = null;
#if !FEATURE_PAL
// For ACL's, get the security descriptor from the EventWaitHandleSecurity.
if (eventSecurity != null) {
secAttrs = new Win32Native.SECURITY_ATTRIBUTES();
secAttrs.nLength = (int)Marshal.SizeOf(secAttrs);
byte[] sd = eventSecurity.GetSecurityDescriptorBinaryForm();
byte* pSecDescriptor = stackalloc byte[sd.Length];
Buffer.memcpy(sd, 0, pSecDescriptor, 0, sd.Length);
secAttrs.pSecurityDescriptor = pSecDescriptor;
}
#endif
SafeWaitHandle _handle = null;
Boolean isManualReset;
switch(mode)
{
case EventResetMode.ManualReset:
isManualReset = true;
break;
case EventResetMode.AutoReset:
isManualReset = false;
break;
default:
throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag",name));
};
_handle = Win32Native.CreateEvent(secAttrs, isManualReset, initialState, name);
int errorCode = Marshal.GetLastWin32Error();
if (_handle.IsInvalid)
{
_handle.SetHandleAsInvalid();
if(null != name && 0 != name.Length && Win32Native.ERROR_INVALID_HANDLE == errorCode)
throw new WaitHandleCannotBeOpenedException(Environment.GetResourceString("Threading.WaitHandleCannotBeOpenedException_InvalidHandle",name));
__Error.WinIOError(errorCode, name);
}
createdNew = errorCode != Win32Native.ERROR_ALREADY_EXISTS;
SetHandleInternal(_handle);
}
private EventWaitHandle(SafeWaitHandle handle)
{
SetHandleInternal(handle);
}
[SecurityPermissionAttribute(SecurityAction.LinkDemand,Flags=SecurityPermissionFlag.UnmanagedCode)]
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
public static EventWaitHandle OpenExisting(string name)
{
#if !FEATURE_PAL
return OpenExisting(name, EventWaitHandleRights.Modify | EventWaitHandleRights.Synchronize);
}
[SecurityPermissionAttribute(SecurityAction.LinkDemand,Flags=SecurityPermissionFlag.UnmanagedCode)]
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
public static EventWaitHandle OpenExisting(string name, EventWaitHandleRights rights)
{
#endif // !FEATURE_PAL
if (name == null)
{
throw new ArgumentNullException("name", Environment.GetResourceString("ArgumentNull_WithParamName"));
}
if(name.Length == 0)
{
throw new ArgumentException(Environment.GetResourceString("Argument_EmptyName"), "name");
}
if(null != name && System.IO.Path.MAX_PATH < name.Length)
{
throw new ArgumentException(Environment.GetResourceString("Argument_WaitHandleNameTooLong",name));
}
#if FEATURE_PAL
SafeWaitHandle myHandle = Win32Native.OpenEvent(Win32Native.EVENT_MODIFY_STATE | Win32Native.SYNCHRONIZE, false, name);
#else
SafeWaitHandle myHandle = Win32Native.OpenEvent((int) rights, false, name);
#endif
if (myHandle.IsInvalid)
{
int errorCode = Marshal.GetLastWin32Error();
if(Win32Native.ERROR_FILE_NOT_FOUND == errorCode || Win32Native.ERROR_INVALID_NAME == errorCode)
throw new WaitHandleCannotBeOpenedException();
if(null != name && 0 != name.Length && Win32Native.ERROR_INVALID_HANDLE == errorCode)
throw new WaitHandleCannotBeOpenedException(Environment.GetResourceString("Threading.WaitHandleCannotBeOpenedException_InvalidHandle",name));
//this is for passed through Win32Native Errors
__Error.WinIOError(errorCode,"");
}
return new EventWaitHandle(myHandle);
}
public bool Reset()
{
bool res = Win32Native.ResetEvent(safeWaitHandle);
if (!res)
__Error.WinIOError();
return res;
}
public bool Set()
{
bool res = Win32Native.SetEvent(safeWaitHandle);
if (!res)
__Error.WinIOError();
return res;
}
#if !FEATURE_PAL
public EventWaitHandleSecurity GetAccessControl()
{
return new EventWaitHandleSecurity(safeWaitHandle, AccessControlSections.Access | AccessControlSections.Owner | AccessControlSections.Group);
}
public void SetAccessControl(EventWaitHandleSecurity eventSecurity)
{
if (eventSecurity == null)
throw new ArgumentNullException("eventSecurity");
eventSecurity.Persist(safeWaitHandle);
}
#endif
}
}
再来看下ManualResetEvent的代码
namespace System.Threading {
using System;
using System.Security.Permissions;
using System.Runtime.InteropServices;
[HostProtection(Synchronization=true, ExternalThreading=true)]
[System.Runtime.InteropServices.ComVisible(true)]
public sealed class ManualResetEvent : EventWaitHandle
{
public ManualResetEvent(bool initialState) : base(initialState,EventResetMode.ManualReset){}
}
}
其实这两个的差别就是AutoResetEvent是base(initialState,EventResetMode.AutoReset)而ManualResetEvent是base(initialState,EventResetMode.ManualReset).
AutoResetEvent是操作单个线程的,而ManualResetEvent可以操作多个线程.
AutoResetEvent开发示例
在主线程运行后,新开一个新线程,由新线程来控制主线程的等待和执行.
类关系图
代码实现
//===============================================================================
//作者:Spring Yang
//日期:2011-10-12
//===============================================================================
namespace TestMultipleThread
{
using System;
using System.Collections.Generic;
using System.Threading;
public class ThreadWork
{
private MainContext mainContext;
private ContextCache contextCache;
public ThreadWork()
{
contextCache = ContextCache.GetContextCache();
mainContext = contextCache.GetContextByUserId("001");
}
public void Sum()
{
Console.WriteLine(string.Format("this is thread ID {0} execute", Thread.CurrentThread.ManagedThreadId));
for (int i = 0; i < 10; i++)
{
Thread.Sleep(10);
}
if (mainContext != null && mainContext.IsCrawlWait == false)
{
mainContext.IsNTFSEvent = true;
while (mainContext.IsCrawlWait)
{
Thread.Sleep(25);
}
Console.WriteLine("main thread is wait");
Thread.Sleep(10000);
Console.WriteLine("main thread is start");
mainContext.IsNTFSEvent = false;
mainContext.SetThread();
}
}
}
class Program
{
public static void Main()
{
MainContext mainContext = new MainContext("001");
mainContext.Save();
//新开一个线程
ThreadWork threadWork = new ThreadWork();
ThreadStart myThreadDelegate = new ThreadStart(threadWork.Sum);
Thread myThread = new Thread(myThreadDelegate);
myThread.Start();
for (int i = 0; i < 100; i++)
{
if (mainContext.IsNTFSEvent)
mainContext.ThreadWait();
Thread.Sleep(10);
}
Console.WriteLine("main Thread continue");
Thread.Sleep(100000);
}
public void WriteMessage()
{
Console.WriteLine("Stop the main thread.");
}
}
public class MainContext
{
public string UserId { get; set; }
//设置 AutoResetEvent
public AutoResetEvent WaitEvent;
public ContextCache contextCache;
//设置线程等待
public void ThreadWait()
{
if (WaitEvent == null) WaitEvent = new AutoResetEvent(false);
if (IsNTFSEvent)
{
IsCrawlWait = true;
WaitEvent.WaitOne();
}
}
public MainContext(string userID)
{
UserId = userID;
IsCrawlWait = false;
IsNTFSEvent = false;
contextCache = ContextCache.GetContextCache();
}
public void Save()
{
contextCache.Save(UserId, this);
}
public void SetThread()
{
if (!IsNTFSEvent && IsCrawlWait)
{
IsCrawlWait = false;
WaitEvent.Set();
}
}
public bool IsCrawlWait { get; set; }
public bool IsNTFSEvent { get; set; }
}
//单例模式,保存到内存中
public class ContextCache
{
public Dictionary<string, MainContext> dicMainContext = new Dictionary<string, MainContext>();
public void Save(string userId, MainContext mainContext)
{
dicMainContext.Add(userId, mainContext);
}
private static ContextCache singleContextCache;
public MainContext GetContextByUserId(string userId)
{
MainContext context;
dicMainContext.TryGetValue(userId, out context);
return context;
}
private ContextCache()
{
}
public static ContextCache GetContextCache()
{
if (singleContextCache == null)
{
singleContextCache = new ContextCache();
}
return singleContextCache;
}
}
}
运行结果
转载地址:http://www.cnblogs.com/springyangwc/archive/2011/10/12/2208991.html