C#各种同步方法 lock, Monitor,Mutex, Semaphore, Interlocked, ReaderWriterLock,AutoResetEvent, ManualResetEvent

看下组织结构:

System.Object
  System.MarshalByRefObject
    System.Threading.WaitHandle
      System.Threading.Mutex
      System.Threading.Semaphore
      System.Threading.EventWaitHandle
        System.Threading.ManualResetEvent

        System.Threading.AutoResetEvent

System.Object

  System.Threading.Interlocked
  System.Threading.Monitor

  System.Threading.ReaderWriterLock

1, lock 关键字其实就是对 Monitor 类的 Enter()和 Exit()方法的封装。通过 try......catch......finally 语句块确保在 lock 语句块结束后执行 Monitor.Exit()方法,释放互斥锁。下面2段代码等价:

lock(locker)
{
  //do something
}

Monitor.Enter(locker);
try
{
    // do something
}
finally
{
    Monitor.Exit(locker);
}

2,

Monitor类通过向单个线程授予对象锁来控制对对象的访问。对象锁提供限制访问临界区的能力。当一个线程拥有对象的锁时,其他任何线程都不能获取该锁。还可以使用 Monitor 来确保不会允许其他任何线程访问正在由锁的所有者执行的应用程序代码节,除非另一个线程正在使用其他的锁定对象执行该代码。通过对 lock 关键字的分析我们知道,lock 就是对 Monitor 的 Enter 和 Exit 的一个封装,而且使用起来更简洁,因此 Monitor 类的 Enter()和 Exit()方法的组合使用可以用 lock 关键字替代。 
   Monitor 类的常用方法: 
        TryEnter(): 
            能够有效的解决长期死等的问题,如果在一个并发经常发生,而且持续时间长的环境中使用 TryEnter,可以有效防止死锁或者长时间的等待。比如我们可以设置一个等待时间 bool gotLock = Monitor.TryEnter(myobject,1000),让当前线程在等待 1000 秒后根据返回的 bool 值来决定是否继续下面的操作。

Wait() :

释放对象上的锁以便允许其他线程锁定和访问该对象。在其他线程访问对象时,调用线程将等待。脉冲信号用于通知等待线程有关对象状态的更改。 
        Pulse(): 
        PulseAll(): 
            向一个或多个等待线程发送信号。该信号通知等待线程锁定对象的状态已更改,并且锁的所有者准备释放该锁。等待线程被放置在对象的就绪队列中以便它可以最后接收对象锁。一旦线程拥有了锁,它就可以检查对象的新状态以查看是否达到所需状态。注意:Pulse、PulseAll 和 Wait 方法必须从同步的代码块内调用。

       static object locker = new object();
        static bool isHave = false;

        static void Produce()
        {
            lock (locker)
            {
                while (true)
                {
                    //如果已有产品,则等待消费完成
                    if (isHave)
                        Monitor.Wait(locker);
                    Console.WriteLine("生产一个");
                    Thread.Sleep(1000);
                    isHave = true;
                    Monitor.Pulse(locker);
                }
            }
        }
        static void Consume()
        {
            lock (locker)
            {
                while (true)
                {
                    //如果没有产品,则等待生产完成
                    if (!isHave)
                        Monitor.Wait(locker);
                    Console.WriteLine("消费一个");
                    Thread.Sleep(500);
                    isHave = false;
                    Monitor.Pulse(locker);
                }
            }
        }

在main函数中调用:

            new Thread(Produce).Start();
            new Thread(Consume).Start();

3, Mutex互斥体

public class Test
    {
        // Create a new Mutex. The creating thread does not own the
        // Mutex.
        private static Mutex mut = new Mutex();

        public static void MyThreadProc()
        {
            for (int i = 0; i < 2; i++)
            {
                UseResource();
            }
        }

        // This method represents a resource that must be synchronized
        // so that only one thread at a time can enter.
        private static void UseResource()
        {
            // Wait until it is safe to enter.
            mut.WaitOne();

            Console.WriteLine("{0} has entered the protected area",
                Thread.CurrentThread.Name);

            // Place code to access non-reentrant resources here.

            // Simulate some work.
            Thread.Sleep(500);

            Console.WriteLine("{0} is leaving the protected area\r\n",
                Thread.CurrentThread.Name);

            // Release the Mutex.
            mut.ReleaseMutex();
        }
    }

Test test = new Test();
            for (int i = 0; i < 3; i++)
            {
                Thread myThread = new Thread(new ThreadStart(Test.MyThreadProc));
                myThread.Name = String.Format("Thread{0}", i + 1);
                myThread.Start();
            }

mutex还可以判断系统是否已经有一个进程存在。

4,Semaphore 信号量

static Semaphore sph = new Semaphore(0, 3);
        static void TProc()
        {
            while (true)
            {
                if (sph.WaitOne(500, false))
                {
                    try
                    {
                        Console.WriteLine("thread" + Thread.CurrentThread.Name + ":enter");
                        Thread.Sleep(1000);
                    }
                    finally
                    {
                        sph.Release();
                        Console.WriteLine("thread" + Thread.CurrentThread.Name + ":exit");
                    }
                }
                else
                {
                    Console.WriteLine("thread" + Thread.CurrentThread.Name + ":time out");
                }
            }
        }

Thread t = null;
            for (int i = 0; i < 2; i++)
            {
                t = new Thread(TProc);
                t.Name = i.ToString();
                t.Start();
            }
            Console.WriteLine("main sleep 4s");
            Thread.Sleep(4000);
            sph.Release(2);

5,Interlocker类为多个线程共享的变量提供原子操作,它是一个静态类,主要的成员方法如下:
Add:以原子操作的形式,添加两个整数并用两者的和替换第一个整数。
Exchange:以原子操作的形式将变量设置为指定的值,并返回先前值
CompareExchange:比较两个值是否相等,如果相等,则替换其中一个值
Equals:确定两个Object 实例是否相等
Increment:以原子操作的形式递增指定变量的值并存储结果
Decrement:以原子操作的形式递减指定变量的值并存储结果
Read:返回一个以原子操作形式加载的 64 位值

Interlocked.CompareExchange(ref obj, new object(), null);

6, ReaderWriterLock

static ReaderWriterLock rwLock = new ReaderWriterLock();
static object locker = new object();
static void Main(string[] args)
{
    Thread t = null;
    for(int i = 0; i < 2;i++)
    {
        t = newThread(Writer);
        t.Name =i.ToString();
        t.Start();
    }
    for(int i = 0; i<3;i++)
    {
        t = newThread(Reader);
        t.Name =i.ToString();
        t.Start();
    }
    Console.ReadLine();
}
static void Writer()
{
    while(true)
    {
        try
        {
            rwLock.AcquireWriterLock(3000);
            Console.WriteLine("writer:"+ Thread.CurrentThread.Name + " is enter" + "WriterSeqNum:" +rwLock.WriterSeqNum.ToString());
            try
            {
                Thread.Sleep(5000);
            }
            finally
            {
                rwLock.ReleaseWriterLock();
                Console.WriteLine("writer:"+ Thread.CurrentThread.Name + " is exit");
            }
        }
        catch(ApplicationException)
        {
            Console.WriteLine("writer:"+ Thread.CurrentThread.Name + " wait time out");
        }
    }
}
static void Reader()
{
    while (true)
    {
        rwLock.AcquireReaderLock(-1);
        Console.WriteLine("reader:"+ Thread.CurrentThread.Name + " is enter" + "WriterSeqNum:" +rwLock.WriterSeqNum.ToString());
        try
        {
            Thread.Sleep(3000);
        }
        finally
        {
            Console.WriteLine("reader:"+ Thread.CurrentThread.Name + " is exit");
            rwLock.ReleaseReaderLock();
        }
    }
}

7,AutoResetEvent 自动重置事件

在构造事件对象时需要指定initialState参数是True还是False,以指示事件的初始状态是有信号还是无信号

当一个自动重置事件得到信号时,等待该事件的线程中只有一个线程变为可调度线程,当手动重置对象得到信号时,等待该事件的所有线程均变为可调度线程

class Example
{
    private static AutoResetEvent event_1 = new AutoResetEvent(true);
    private static AutoResetEvent event_2 = new AutoResetEvent(false);

    static void Main()
    {
        Console.WriteLine("Press Enter to create three threads and start them.\r\n" +
                          "The threads wait on AutoResetEvent #1, which was created\r\n" +
                          "in the signaled state, so the first thread is released.\r\n" +
                          "This puts AutoResetEvent #1 into the unsignaled state.");
        Console.ReadLine();

        for (int i = 1; i < 4; i++)
        {
            Thread t = new Thread(ThreadProc);
            t.Name = "Thread_" + i;
            t.Start();
        }
        Thread.Sleep(250);

        for (int i = 0; i < 2; i++)
        {
            Console.WriteLine("Press Enter to release another thread.");
            Console.ReadLine();
            event_1.Set();
            Thread.Sleep(250);
        }

        Console.WriteLine("\r\nAll threads are now waiting on AutoResetEvent #2.");
        for (int i = 0; i < 3; i++)
        {
            Console.WriteLine("Press Enter to release a thread.");
            Console.ReadLine();
            event_2.Set();
            Thread.Sleep(250);
        }

        // Visual Studio: Uncomment the following line.
        //Console.Readline();
    }

    static void ThreadProc()
    {
        string name = Thread.CurrentThread.Name;

        Console.WriteLine("{0} waits on AutoResetEvent #1.", name);
        event_1.WaitOne();
        Console.WriteLine("{0} is released from AutoResetEvent #1.", name);

        Console.WriteLine("{0} waits on AutoResetEvent #2.", name);
        event_2.WaitOne();
        Console.WriteLine("{0} is released from AutoResetEvent #2.", name);

        Console.WriteLine("{0} ends.", name);
    }
}

8, ManualResetEvent 手动重置事件

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

        Console.WriteLine(name + " ends.");
    }
}

9, .NET 在一些集合类,如 Queue、ArrayList、HashTable 和 Stack,已经提供了一个供 lock 使用的对象 SyncRoot。

Queue q = new Queue();
                lock (q.SyncRoot)
                {
                    foreach (object item in q)
                    {
                        //do something
                    }
                }  

参考资料:http://blog.csdn.net/zzy7075/article/details/29842165

时间: 2024-11-07 23:26:15

C#各种同步方法 lock, Monitor,Mutex, Semaphore, Interlocked, ReaderWriterLock,AutoResetEvent, ManualResetEvent的相关文章

c# 中的 lock monitor mutex Semaphore 的比较

直接贴文章好了,这些大神都讲解的很清楚: c# 中的 mutex https://msdn.microsoft.com/en-us/library/system.threading.mutex(v=vs.110).aspx c# 中的 lock monitor mutex Semaphore  的比较 https://abhijit-k-adhikari.me/2012/04/17/lock-monitor-mutex-semaphore/ https://www.onlinebuff.com/

线程系列08,实现线程锁的各种方式,使用lock,Montor,Mutex,Semaphore以及线程死锁

当涉及到多线程共享数据,需要数据同步的时候,就可以考虑使用线程锁了.本篇体验线程锁的各种用法以及线程死锁.主要包括: ※ 使用lock处理数据同步※ 使用Monitor.Enter和Monitor.Exit处理数据同步※ 使用Mutex处理进程间数据同步※ 使用Semaphore处理数据同步※ 线程死锁 □ 使用lock处理数据同步 假设有一个类,主要用来计算该类2个字段的商,在计算商的方法之内让被除数自减,即被除数有可能为零.使用lock语句块保证每次只有一个线程进入该方法. class Th

C# 多线程(lock,Monitor,Mutex,同步事件和等待句柄)

本文来自:http://www.cnblogs.com/SkySoot/archive/2012/04/02/2430295.html 本篇从 Monitor,Mutex,ManualResetEvent,AutoResetEvent,WaitHandler 的类关系图开始,希望通过本篇的介绍能对常见的线程同步方法有一个整体的认识,而对每种方式的使用细节,适用场合不会过多解释. 让我们来看看这几个类的关系图: 1. lock 关键字     lock 是 C# 关键词,它将语句块标记为临界区,确

【转】多线程:C#线程同步lock,Monitor,Mutex,同步事件和等待句柄(上)

本篇从Monitor,Mutex,ManualResetEvent,AutoResetEvent,WaitHandler的类关系图开始,希望通过 本篇的介绍能对常见的线程同步方法有一个整体的认识,而对每种方式的使用细节,适用场合不会过多解释.让我们来看看这几个类的关系图: 1.lock关键字      lock是C#关键词,它将语句块标记为临界区,确保当一个线程位于代码的临界区时,另一个线程不进入临界区.如果其他线程试图进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放.方法是获取给定

C#多线程:深入了解线程同步lock,Monitor,Mutex,同步事件和等待句柄(中)

本篇继续介绍WaitHandler类及其子类 Mutex,ManualResetEvent,AutoResetEvent的用法..NET中线程同步的方式多的让人看了眼花缭乱,究竟该怎么去理解呢?其实,我们抛开.NET环境看线程同步,无非是执行两种操作:一是互斥/加锁,目的是保证临界区代码操作的"原子性":另一种是信号灯操作,目的是保证多个线程按照一定顺序执行,如生产者线程要先于消费者线程执行..NET中线程同步的类无非是对这两种方式的封装,目的归根结底都可以归结为实现互斥/ 加锁或者是

C#使用Monitor类、Lock和Mutex类进行多线程同步

在多线程中,为了使数据保持一致性必须要对数据或是访问数据的函数加锁,在数据库中这是很常见的,但是在程序中由于大部分都是单线程的程序,所以没有加锁的必要,但是在多线程中,为了保持数据的同步,一定要加锁,好在Framework中已经为我们提供了三个加锁的机制,分别是Monitor类.Lock关键字和Mutex类. 其中Lock关键词用法比较简单,Monitor类和Lock的用法差不多.这两个都是锁定数据或是锁定被调用的函数.而Mutex则多用于锁定多线程间的同步调用.简单的说,Monitor和Loc

synchronization mutex &amp;semaphore completion &amp;example

Linux offers two kinds of semaphores: ? Kernel semaphores, which are used by kernel control paths ? System V IPC semaphores, which are used by User Mode processes struct semaphore { spinlock_t lock; unsigned int count; struct list_headwait_list; }; s

线程系列07,使用lock语句块或Interlocked类型方法保证自增变量的数据同步

假设多个线程共享一个静态变量,如果让每个线程都执行相同的方法每次让静态变量自增1,这样的做法线程安全吗?能保证自增变量数据同步吗?本篇体验使用lock语句块和Interlocked类型方法保证自增变量的数据同步. □ 线程不安全.数据不同步的做法 class Program { static int sum = 0; static void Main(string[] args) { Stopwatch watch = new Stopwatch(); watch.Start(); Parall

lock了mutex的线程退出了却没有unlock时会怎么样?

https://stackoverflow.com/questions/4424193/what-happens-to-mutex-when-the-thread-which-acquired-it-exits?noredirect=1&lq=1 解释当一个lock了mutex的线程退出了,却没有主动unlock这个mutex时,会发生什么 If you created a robust mutex by setting up the right attributes before callin