在C#里面用于实现线程同步的常用类有如下几类:
1、Mutex类(互斥器),Monitor类,lock方法
2、ManualResetEvent类,AutoResetEvent类(这两个都是由EventWaitHandle类派生出来的)
3、ReaderWriterLock类
分析如下:
1、ManualResetEvent类:
ManualResetEvent 允许线程通过发信号互相通信。通常,此通信涉及一个线程在其他线程进行之前必须完成的任务。当一个线程开始一个活动(此活动必须完成后,其他线程才能开始)时,它调用 Reset 以将 ManualResetEvent 置于非终止状态(阻塞,无信号状态),此线程可被视为控制 ManualResetEvent。调用 ManualResetEvent 上的 WaitOne 的线程将阻止,并等待信号。当控制线程完成活动时,它调用 Set 以发出等待线程可以继续进行的信号。并释放所有等待线程。一旦它被终止,ManualResetEvent 将保持终止状态(非阻塞,有信号状态)(即对 WaitOne 的调用的线程将立即返回,并不阻塞),直到它被手动重置。可以通过将布尔值传递给构造函数来控制 ManualResetEvent 的初始状态,如果初始状态处于终止状态,为 true;否则为 false。
ManualResetEvent对象只能拥有两种状态之一:有信号(True)或无信号(false)。ManualResetEvent类继承于WaitHandle类,其构造函数的参数可确定对象的初始状态。Set()和Reset()方法返回一个布尔值,表示是否进行了成功的修改。
ManualResetEvent
Set()方法将状态设置为有信号
Reset()将其设置为无信号
WaitOne()将阻塞到其有信号为止,若调用WaitOne的时刻就是有信号的,将不会阻塞
个人理解:
ManualResetEvent 初始状态,设置为ture,表示终止状态(非阻塞,有信号状态),设置为False表示未终止的状态(阻塞,无信号状态)。
当调用Reset()时,表明当前ManualResetEvent置于非终止状态(阻塞,无信号状态)。
当调用Set()时,表明当前ManualResetEvent置于终止状态(非阻塞,有信号状态)。
当调用WaitOne()时,表明当前线程将阻止,并等待信号。
static void Main(string[] args)
{
ManualResetEvent mansig;
mansig = new ManualResetEvent(false); //非终止状态(阻塞,无信号状态)
bool state = mansig.WaitOne(9000, true);
Console.WriteLine("ManualResetEvent After WaitOne" + state);
mansig.Reset();
state = mansig.WaitOne(9000, true);
Console.WriteLine("ManualResetEvent After WaitOne" + state);
mansig.Set();
state = mansig.WaitOne(9000, true);
Console.WriteLine("ManualResetEvent After WaitOne" + state);
Console.Read();
}
运行结果:
static void Main(string[] args)
{
ManualResetEvent mansig;
mansig = new ManualResetEvent(true); //终止状态(非阻塞,有信号状态)
bool state = mansig.WaitOne(9000, true);
Console.WriteLine("ManualResetEvent After WaitOne" + state);
mansig.Reset();
state = mansig.WaitOne(9000, true);
Console.WriteLine("ManualResetEvent After WaitOne" + state);
mansig.Set();
state = mansig.WaitOne(9000, true);
Console.WriteLine("ManualResetEvent After WaitOne" + state);
Console.Read();
}
运行结果:
class Program
{
public static ManualResetEvent mre = new ManualResetEvent(false); //阻塞状态
public static void trmain()
{
Thread tr = Thread.CurrentThread;
Console.WriteLine(tr.Name + " 开始第一波等待");
mre.WaitOne(); //等到什么时候呢?等到mre.Set()被调用
Console.WriteLine(tr.Name + " 第一波启动t");
mre.Reset(); //再次重置
Console.WriteLine(tr.Name + " 开始第二波等待");
mre.WaitOne(); //再次等待
Console.WriteLine(tr.Name + " 第二波启动");
for (int x = 0; x < 10; x++)
{
Thread.Sleep(1000);
Console.WriteLine(tr.Name + ": " + x);
}
}
static void Main(string[] args)
{
Thread thrd1 = new Thread(new ThreadStart(trmain));
thrd1.Name = "thread1";
thrd1.Start();
Thread thrd2 = new Thread(new ThreadStart(trmain));
thrd2.Name = "thread2";
thrd2.Start();
for (int x = 0; x < 10; x++)
{
Thread.Sleep(900);
Console.WriteLine("Main :" + x);
if (5 == x)
{
mre.Set(); //子线程的mre.WaitOne()可以执行了。第一次等待进程
}
}
while (thrd1.IsAlive)
{
Thread.Sleep(1000);
Console.WriteLine("Main: waiting for thread to stop...");
mre.Set(); //第二次通知等待进程
}
Console.Read();
}
}
AutoResetEvent和ManualResetEvent区别:在.Net多线程编程中,AutoResetEvent和ManualResetEvent这两个类经常用到, 他们的用法很类似,但也有区别。Set方法将信号置为发送状态,Reset方法将信号置为不发送状态,WaitOne等待信号的发送。可以通过构造函数的参数值来决定其初始状态,若为true则非阻塞状态,为false为阻塞状态。如果某个线程调用WaitOne方法,则当信号处于发送状态时,该线程会得到信号, 继续向下执行。其区别就在调用后,AutoResetEvent.WaitOne()每次只允许一个线程进入,当某个线程得到信号后,AutoResetEvent会自动又将信号置为不发送状态,则其他调用WaitOne的线程只有继续等待.也就是说,AutoResetEvent一次只唤醒一个线程;而ManualResetEvent则可以唤醒多个线程,因为当某个线程调用了ManualResetEvent.Set()方法后,其他调用WaitOne的线程获得信号得以继续执行,而ManualResetEvent不会自动将信号置为不发送。也就是说,除非手工调用了ManualResetEvent.Reset()方法,则ManualResetEvent将一直保持有信号状态,ManualResetEvent也就可以同时唤醒多个线程继续执行。
另外WaitOne方法也可以带两个参数: WaitOne (int millisecondsTimeout,bool exitContext) millisecondsTimeout:等待的毫秒数,或为 Timeout.Infinite (-1),表示无限期等待。 exitContext:为 true,则等待之前先退出上下文的同步域(如果在同步上下文中),然后在稍后重新获取它;否则为false。 就是说,等待是可以加上一个期限的,如果等待的同步对象一直都不Set()的话,那么程序就会卡死,所以在WaitOne方法里面可以放置一个时间期限,单位是毫秒。
//1.AutoResetEvent,调用一次Set()只能继续一个阻塞线程
//2.AutoResetEvent调用Set()后自动Reset()
static void Main(string[] args)
{
Thread t = null;
AutoResetEvent Event = new AutoResetEvent(false);
for (int i = 0; i < 2; i++)
{
t = new Thread(() =>
{
while (true)
{
//阻塞当前线程
Event.WaitOne();
Console.WriteLine("我是线程:" + Thread.CurrentThread.Name);
Thread.Sleep(1000);
}
});
t.Name = i + "";
t.Start();
}
//5秒后允许一个等待的线程继续。当前允许的是线程1
Thread.Sleep(5000);
Event.Set();
//5秒后允许一个等待的线程继续。当前允许的是线程2
Thread.Sleep(5000);
Event.Set();
//PS:如果使用AutoResetEvent的WaitOne()将5个线程阻塞,则需要调用5次Set()才能恢复5;如果再次阻塞时,不需要手动调用Reset();
Console.ReadLine();
}
//1.ManualResetEvent,调用一次Set()允许继续全部阻塞线程,这是和AutoResetEvent的区别
//2.ManualResetEvent调用Set()后需要手动Reset(),将信号 设置为非终止状态,只有非终止状态线程中调用WaitOne()才能导所在的致线程阻止。
static void Main(string[] args)
{
Thread t = null;
//初始化非终止状态,WaitOne()可以直接阻塞所在的线程
ManualResetEvent Event = new ManualResetEvent(false);
for (int i = 0; i < 2; i++)
{
t = new Thread(() =>
{
while (true)
{
//阻塞当前线程
Event.WaitOne();
Console.WriteLine("我是线程:" + Thread.CurrentThread.Name);
Event.Reset();
Thread.Sleep(1000);
}
});
t.Name = i + "";
t.Start();
}
//5秒后允许所有阻塞的线程继续。
Thread.Sleep(5000);
Event.Set();
//PS:如果使用ManualResetEvent将5个线程阻塞,则需要调用1次Set(),将允许所有阻塞的线程继续执行;如果再次阻塞时,则需要手动调用Reset();
Console.ReadLine();
}
public class Example
{
// mre is used to block and release threads manually. It is
// created in the unsignaled state.
private static ManualResetEvent mre = new ManualResetEvent(false);
static void Main()
{
Console.WriteLine("\nStart 3 named threads that block on a ManualResetEvent:\n");
for (int i = 0; i <= 2; i++)
{
Thread t = new Thread(ThreadProc);
t.Name = "Thread_" + i;
t.Start();
}
Thread.Sleep(500);
Console.WriteLine("\nWhen all three threads have started, press Enter to call Set()" +
"\nto release all the threads.\n");
Console.ReadLine();
mre.Set();
Thread.Sleep(500);
Console.WriteLine("\nWhen a ManualResetEvent is signaled, threads that call WaitOne()" +
"\ndo not block. Press Enter to show this.\n");
Console.ReadLine();
for (int i = 3; i <= 4; i++)
{
Thread t = new Thread(ThreadProc);
t.Name = "Thread_" + i;
t.Start();
}
Thread.Sleep(500);
Console.WriteLine("\nPress Enter to call Reset(), so that threads once again block" +
"\nwhen they call WaitOne().\n");
Console.ReadLine();
mre.Reset();
// Start a thread that waits on the ManualResetEvent.
Thread t5 = new Thread(ThreadProc);
t5.Name = "Thread_5";
t5.Start();
Thread.Sleep(500);
Console.WriteLine("\nPress Enter to call Set() and conclude the demo.");
Console.ReadLine();
mre.Set();
// If you run this example in Visual Studio, uncomment the following line:
//Console.ReadLine();
}
private static void ThreadProc()
{
string name = Thread.CurrentThread.Name;
Console.WriteLine(name + " starts and calls mre.WaitOne()");
mre.WaitOne(1000, true);
Console.WriteLine(name + " ends.");
}
}
运行结果: