AutoResetEvent 允许线程通过发信号互相通信。通常,此通信涉及线程需要独占访问的资源(MSDN)
访问完独占资源后的线程,通过发送信号通知其它等待线程可以开始抢占资源了,最终已独占的形式访问资源。
AutoResetEvent 初始化时可以设置 new AutoResetEvent (False) 即刚开始无信号,所有等待线程都在等待信号的发出,如为True,则刚开始将有一个线程能马上获得信号进入独占资源直到发出信号。
AutoResetEvent 初始化时如果为True 则有信号,否则为无信号,当每次Set()的时候会自动释放一个等待的线程,与ManualResetEvent不同的是: 顾名思义 AutoResetEvent :在释放一个等待线程后,此同步事件会在发出相应的信号时自动重置事件等待句柄,自动重置事件通常用来一次为一个线程提供对资源的独占访问。而ManualResetEvent则是需要手动重置事件等待句柄,所以并不能保证对资源的独占访问。
class Program
{
private static AutoResetEvent AutoReset = new AutoResetEvent( true );
static void Main( string [] args)
{
// 自动重置
AutoResetEventText();
}
static void AutoResetEventText()
{
for ( int i = 0 ; i < 4 ; i ++ )
{
Thread tr = new Thread(doing);
tr.Start(i);
}
Console.Read();
}
static void doing( object threadName)
{
AutoReset.WaitOne();
int i = 0 ;
while (i < 10 )
{
Thread.Sleep( 10 );
Console.WriteLine(threadName + " " + i ++ );
}
AutoReset.Set();
Console.WriteLine(threadName + " over " );
}
}
为多线程提供原子操作。
在大多数计算机上,增加变量操作不是一个原子操作,需要执行下列步骤:1、将实例变量中的值加载到寄存器中。
2、增加或减少该值。
3、在实例变量中存储该值。(来自MSDN)
原子操作保证了在执行以上三步操作期间不会有其他线程改变变量的值。
上面代码的 Interlocked实现
class Program
{
private static int Flag = 0 ;
static void Main( string [] args)
{
CompareExchange();
}
static void CompareExchange()
{
for ( int i = 0 ; i < 4 ; i ++ )
{
Thread tr = new Thread(doing);
tr.Start(i);
}
Console.Read();
}
static void doing( object threadName)
{
// 判断Flag值为0则线程可以进入。
while (Interlocked.CompareExchange( ref Flag, 1 , 0 ) != 0 )
{
Thread.Sleep( 500 );
}
int i = 0 ;
while (i < 10 )
{
Thread.Sleep( 100 );
Console.WriteLine(threadName + " " + i ++ );
}
Console.WriteLine(threadName + " over " );
// 还原Flag值为0
Interlocked.CompareExchange( ref Flag, 0 , 1 );
}
}
限制可同时访问某一资源或资源池的线程数。
信号量的计数在每次线程进入信号量时减小,在线程释放信号量时增加。当计数为零时,后面的请求将被阻塞,直到有其他线程释放信号量。当所有的线程都已释放信号量时,计数达到创建信号量时所指定的最大值。(MSDN)
上面代码的Semaphore实现
class Program
{
// 初始化为一个信号量,且最大信号量为1,所以当 _pool.Release(2)时会报错。
private static Semaphore _pool = new Semaphore( 1 , 1 );
static void Main( string [] args)
{
CompareExchange();
Console.Read();
}
static void CompareExchange()
{
for ( int i = 0 ; i < 4 ; i ++ )
{
Thread tr = new Thread(doing);
tr.Start(i);
}
}
static void doing( object threadName)
{
_pool.WaitOne();
int i = 0 ;
while (i < 10 )
{
Thread.Sleep( 100 );
Console.WriteLine(threadName + " " + i ++ );
}
Console.WriteLine(threadName + " over " );
// 释放信号量
_pool.Release();
}
}
Lock 关键字将语句块标记为临界区,方法是获取给定对象的互斥锁,执行语句,然后释放该锁。(MSDN)
Lock(关键字),在需要同步的线程中要确保访问的是同一关键字对象。
上面代码的Lock实现
class Program
{
private static object LockObj = new object ();
static void Main( string [] args)
{
Lock();
Console.Read();
}
static void Lock()
{
for ( int i = 0 ; i < 4 ; i ++ )
{
Thread tr = new Thread(doing);
tr.Start(i);
}
}
static void doing( object threadName)
{
lock (LockObj)
{
int i = 0 ;
while (i < 10 )
{
Thread.Sleep( 100 );
Console.WriteLine(threadName + " " + i ++ );
}
Console.WriteLine(threadName + " over " );
}
}
}