c#:线程同步

在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();
    }
}
 
   
  

运行结果:c#:线程同步_第1张图片

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.");
    }
}
运行结果:



 
  
 
  
 
  
 
  
 
 

你可能感兴趣的:(.NET,c#)