1.当多个线程在等待一个 AutoResetEvent的时候,每次调用AutoResetEvent.Set()方法只会唤醒一个线程
2.当多个线程在等待一个ManualResetEvent的时候,每次调用ManualResetEvent.Set()方法会唤醒所有等待的线程
3.当多个线程在等待一个Semaphore的时候,每次调用Semaphore.Release(Int releaseCount)的时候 将唤醒releaseCount个线程
如果调用的是Semaphore.Release() 那么相当于Semaphore.Release(1);
4.AutoResetEvent可以多次调用 Set方法,而Semaphore在每次被调用Release方法的时候会计数,如果这个数字超过了最大限制 ,那么会抛出一个SemaphoreFullException(最大数值可以在构造函数中确定 maximumCount)
AutoResetEvent 和 ManualResetEvent 都是继承 EventWaitHandle : WaitHandle
Semaphore也继承WaitHandle
其中常用的就是WaitOne()
AutoResetEvent 允许线程通过发信号互相通信。通常,此通信涉及线程需要独占访问的资源。
线程通过调用 AutoResetEvent 上的 WaitOne 来等待信号。如果 AutoResetEvent 处于非终止状态,则该线程阻塞,并等待当前控制资源的线程通过调用 Set 发出资源可用的信号。
{AutoResetEvent art=new AutoResetEvent (false);//阻碍线程,等待信号发送{art.set()}。}
调用 Set 向 AutoResetEvent 发信号以释放等待线程。AutoResetEvent 将保持终止状态,直到一个正在等待的线程被释放,然后自动返回非终止状态。如果没有任何线程在等待,则状态将无限期地保持为终止状态。可以通过将一个布尔值传递给构造函数来控制 AutoResetEvent 的初始状态,如果初始状态为终止状态,则为 true(有信号状态);否则为 false(无信号状态)。
AutoResetEvent初始化时,参数设置表示其第一次的使用是否已经是阻塞状态,true表示线程不等待,false表示线程需要等待
每次调用AutoResetEvent的set方法将只会释放一个被阻止的线程,不会同时释放所有被此阻塞的线程。
AutoResetEvent的waitone方法不带参数将无休止等待一个被释放的信号,直到收到信号才继续执行。
带参数表示使用的参数为等待的时间,在等待时间内收到释放信号将返回一个true并继续执行,在参数设定的时间内没收到信号将返回false并继续向下执行。
通俗的来讲只有等myResetEven.Set()成功运行后,myResetEven.WaitOne()才能够获得运行机会;Set是发信号,WaitOne是等待信号,只有发了信号,等待的才会执行。如果不发的话,WaitOne后面的程序就永远不会执行。
ManualResetEvent 将保持终止状态(即对 WaitOne 的调用的线程将立即返回,并不阻塞),直到它被手动重置。可以通过将布尔值传递给构造函数来控制 ManualResetEvent 的初始状态,如果初始状态处于终止状态,为 true;否则为 false。
AutoResetEvent:开启 执行 关闭 {自动控制。每次都只能一个线程运行,立即关闭,其它等待信号} 在发送完信号后会立即置为false,等待信号
ManualResetEvenet:开启 执行 执行 执行 {不阻塞},关闭 {是人工控制}
不同的地方就在于AutoResetEvent的WaitOne()方法执行后会自动又将信号置为不发送状态也就是阻塞状态,当再次遇到WaitOne()方法是又会被阻塞,
而ManualResetEvent则不会,只要线程处于非阻塞状态则无论遇到多少次WaitOne()方法都不会被阻塞,除非调用ReSet()方法来手动阻塞线程。
class MREDemo
{
private ManualResetEvent _mre;
public MREDemo()
{
this._mre = new ManualResetEvent(true);
}
public void CreateThreads()
{
Thread t1 = new Thread(new ThreadStart(Run));
t1.Start();
Thread t2 = new Thread(new ThreadStart(Run));
t2.Start();
}
public void Set()
{
this._mre.Set();
}
public void Reset()
{
this._mre.Reset();
}
private void Run()
{
string strThreadID = string.Empty;
try
{
while (true)
{
// 阻塞当前线程
this._mre.WaitOne();
strThreadID = Thread.CurrentThread.ManagedThreadId.ToString();
Console.WriteLine("Thread(" + strThreadID + ") is running...");
Thread.Sleep(5000);
}
}
catch (Exception ex)
{
Console.WriteLine("线程(" + strThreadID + ")发生异常!错误描述:" + ex.Message.ToString());
}
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("****************************");
Console.WriteLine("输入\"stop\"停止线程运行...");
Console.WriteLine("输入\"run\"开启线程运行...");
Console.WriteLine("****************************\r\n");
MREDemo objMRE = new MREDemo();
objMRE.CreateThreads();
while (true)
{
string input = Console.ReadLine();
if (input.Trim().ToLower() == "stop")
{
Console.WriteLine("线程已停止运行...");
objMRE.Reset();
}
else if (input.Trim().ToLower() == "run")
{
Console.WriteLine("线程开启运行...");
objMRE.Set();
}
}
}
}
信号同步的原理就是等待信号,有信号状态直接过,无信号就等。怎么等?通过调用WaitOnce等。Set设置信号为有信号,ReSet设置状态为无信号。AutoResetEvent就是等完之后会自动变成无信号!与之相反是ManualResetEven。
Manual翻译过来就是手动。需要显式调用 Reset
方法将其重置为无信号状态。
Semaphore sema = new Semaphore(x,y)
有一队人排队上洗手间,人就相当于线程,x为还剩余的位置数量,y为总的位置数量。
WaitOne()方法就相当于人在等待洗手间位置的行为,而Release()方法就相当于一个人从洗手间出来的行为,这里再假设x和y都为5,说明
开始的时候洗手间有5个空位置,且总共只有5个位置,当一队超过5个人的队伍要上洗手间的就排队,首先WaitOne()方法等待,发现有空
位就依次进去,每进去一个空位减一,直到进去5之后个没有空位,这时候后面的人就一直等待,直到进去的人从洗手间出来Release()方
法,空位加一,在等待WaitOne()方法的人发现有空位又进去一个空位减一……如此循环往复。
这里我要说明一点,信号量控制的只是线程同步的量,而不管顺序,这个例子来说线程控制的就是线程同步量为5,也就是同时并发的线程数量为5个,至于是哪个先哪个后不是由这里的信号量决定的。
public class Program
{
static Semaphore sema = new Semaphore(5, 5);
const int cycleNum = 9;
static void Main(string[] args)
{
for(int i = 0; i < cycleNum; i++)
{
Thread td = new Thread(new ParameterizedThreadStart(testFun));
td.Name = string.Format("编号{0}",i.ToString());
td.Start(td.Name);
}
Console.ReadKey();
}
public static void testFun(object obj)
{
sema.WaitOne();
Console.WriteLine(obj.ToString() + "进洗手间:" + DateTime.Now.ToString());
Thread.Sleep(2000);
Console.WriteLine(obj.ToString() + "出洗手间:" + DateTime.Now.ToString());
sema.Release();
}
}