多线程学习笔记之线程同步

1、为多个线程共享的变量提供原子操作。Interlocked

public static class Interlocked

2、互斥锁Mutex 限制只能有一个访问

		Mutex m = new Mutex();			
		m.WaitOne();
        Console.WriteLine("1");
        m.ReleaseMutex();

3、多个访问线程数量锁SemaphoreSlim

		static SemaphoreSlim _semaphore = new SemaphoreSlim(4);//限制4个线程最多同时访问
		//下面方法占用一个
		_semaphore.Wait();

        _semaphore.Release();

4、线程间通知AutoResetEvent

AutoResetEvent myResetEvent = new AutoResetEvent(false);

    如过设置true,那么myResetEvent.WaitOne()就能够在程序一启动就获得运行权;就相当于一启动程序就自动运行了一次myResetEven.Set();
    否则myResetEven.Set()成功运行后,myResetEven.WaitOne()才能够获得运行机会;
false:无信号,子线程的WaitOne方法不会被自动调用
true:有信号,子线程的WaitOne方法会被自动调用myResetEvent.WaitOne();
    当调用myResetEvent.Set();后,waitone后的代码才执行。WaitOne 或是WaitAll 最好都加上超时时间。
    AutoResetEvent在.Net多线程编程中,经常用到。当某个线程调用WaitOne方法后,信号处于发送状态,该线程会得到信号, 程序就会继续向下执行,否则就等待。而且 AutoResetEvent.WaitOne()每次只允许一个线程进入,当某个线程得到信号后,AutoResetEvent会自动又将信号置为不发送状态,其他调用WaitOne的线程只有继续等待.也就是说,AutoResetEvent一次只唤醒一个线程,其他线程还是堵塞。(!!!只能通知到单个线程)
    Reset ():将事件状态设置为非终止状态,导致线程阻止;如果该操作成功,则返回true;否则,返回false。
    Set ():将事件状态设置为终止状态,允许一个或多个等待线程继续;如果该操作成功,则返回true;否则,返回false。
    WaitOne(): 阻止当前线程,直到收到信号。
    WaitOne(TimeSpan, Boolean) :阻止当前线程,直到当前实例收到信号,使用 TimeSpan 度量时间间隔并指定是否在等待之前退出同步域。
    WaitAll():等待全部信号。
注意:AutoResetEvent采用的是内核时间,所以等待时间不能太长。使用后面的ManualResetEventSlim类更好

5、多线程间通知ManualResetEvent

    与AutoResetEvent类似,但是ManualResetEvent是通知所有准备好的线程。ManualResetEvnetSlim的整个工作方式有点像人群通过大门。
    AutoResetEvent事件像一个旋转门,一次只允许一人通过。ManualResetEventSlim是Manual-ResetEvent的混合版本,一直保持大门敞开直到手动调用Reset方法。当调用_mainEvent.Set时,相当于打开了大门从而允许准备好的线程接收信号并继续工作。然而如果线程还处于睡眠状态,没有赶上时间。当调用_mainEvent.Reset相当于关闭了大门。最后一个线程已经准备好执行,但是不得不等待下一个信号,即要等待好几秒钟。

6、CountDownEvent等待一定数量操作的类

		// <summary>
        /// 创建 CountdownEvent 实例,指定达到次数为 2 次
        /// </summary>
        public static CountdownEvent _countdown = new CountdownEvent(2);

        /// <summary>
        ///
        /// </summary>
        /// <param name="message">消息提示</param>
        /// <param name="seconds">时间(秒)</param>
        public static void PerformOperation(string message, int seconds)
        {
            System.Threading.Thread.Sleep(TimeSpan.FromSeconds(seconds));

            Console.WriteLine(message);
			//添加一次执行数量
            _countdown.Signal();
        }
		
		/// <summary>
        /// 输出
        /// </summary>
        public static void Print()
        {
            // Starting two operations

            var t1 = new System.Threading.Thread(() => PerformOperation("Operation 1 is completed", 4));

            var t2 = new System.Threading.Thread(() => PerformOperation("Operation 2 is completed", 8));

            t1.Start();
            t2.Start();

            // 如果 调用 _countdown.Signal() 没达到指定的次数
            // 那么 _countdown.Signal() 将一直阻止,继续等待
            _countdown.Wait();  // 阻止当前线程

            // Both operation have been completed
            _countdown.Dispose();   // 释放资源
        }

7、Barrier 线程指定次数调用SignalAndWait后,自动调用回调函数Barrier

		static void Main(string[] args)
        {
            var t1 = new Thread(() => PlayMusic("the guitarist", "play an amazing solo", 5));
            var t2 = new Thread(() => PlayMusic("the singer", "sing the song", 2));
 
            t1.Start();
            t2.Start();
 
            Console.ReadKey();
        }
		//指定Barrier在线程调用两次SignalAndWait后执行回调函数。
        static Barrier _barrier = new Barrier(2, b => Console.WriteLine("End of phase {0}", b.CurrentPhaseNumber + 1));
 
        static void PlayMusic(string name,string message,int seconds)
        {
            for (int i = 1; i < 3; i++)
            {
                Console.WriteLine("---------------------------------------------");
                Thread.Sleep(TimeSpan.FromSeconds(seconds));
                Console.WriteLine("{0} starts to {1}", name, message);
                Thread.Sleep(TimeSpan.FromSeconds(seconds));
                Console.WriteLine("{0} finishes to {1}", name, message);
                _barrier.SignalAndWait();
            }
        }

8、ReaderWriterLockSlim 线程对集合读写的安全机制,允许多线程同时读取,以及独占用

    ReaderWriterLockSlim 类支持三种锁定模式:Read,Write,UpgradeableRead。这三种模式对应的方法分别是 EnterReadLock,EnterWriteLock,EnterUpgradeableReadLock 。再就是与此对应的 TryEnterReadLock,TryEnterWriteLock,TryEnterUpgradeableReadLock,ExitReadLock,ExitWriteLock,ExitUpgradeableReadLock。
    Read 和 Writer 锁定模式比较简单易懂:Read 模式是典型的共享锁定模式,任意数量的线程都可以在该模式下同时获得锁;Writer 模式则是互斥模式,在该模式下只允许一个线程进入该锁。UpgradeableRead 锁定模式可能对于大多数人来说比较新鲜,但是在数据库领域却众所周知。
备注及注意事项
1、对于同一把锁、多个线程可同时进入读模式。
2、对于同一把锁、同时只允许一个线程进入写模式。
3、对于同一把锁、同时只允许一个线程进入可升级的读模式。
4、通过默认构造函数创建的读写锁是不支持递归的,若想支持递归 可通过构造 ReaderWriterLockSlim(LockRecursionPolicy) 创建实例。
5、对于同一把锁、同一线程不可两次进入同一锁状态(开启递归后可以)
6、对于同一把锁、即便开启了递归、也不可以在进入读模式后再次进入写模式或者可升级的读模式(在这之前必须退出读模式)。
7、再次强调、不建议启用递归。
8、读写锁具有线程关联性,即两个线程间拥有的锁的状态相互独立不受影响、并且不能相互修改其锁的状态。
9、升级状态:在进入可升级的读模式 EnterUpgradeableReadLock后,可在恰当时间点通过EnterWriteLock进入写模式。
10、降级状态:可升级的读模式可以降级为读模式:即在进入可升级的读模式EnterUpgradeableReadLock后, 通过首先调用读取模式EnterReadLock方法,然后再调用 ExitUpgradeableReadLock 方法。
    简单的说,当某个线程进入读取模式时,此时其他线程依然能进入读取模式,假设此时一个线程要进入写入模式,那么他不得不被阻塞。直到读取模式退出为止。
    同样的,如果某个线程进入了写入模式,那么其他线程无论是要写入还是读取,都是会被阻塞的。
进入写入/读取模式有2种方法:
    EnterReadLock尝试进入写入模式锁定状态。
    TryEnterReadLock(Int32) 尝试进入读取模式锁定状态,可以选择整数超时时间。
    EnterWriteLock 尝试进入写入模式锁定状态。
    TryEnterWriteLock(Int32) 尝试进入写入模式锁定状态,可以选择超时时间。
退出写入/读取模式有2种方法:
    ExitReadLock 减少读取模式的递归计数,并在生成的计数为 0(零)时退出读取模式。
    ExitWriteLock 减少写入模式的递归计数,并在生成的计数为 0(零)时退出写入模式。
    为了降低阻塞,可以使用EnterUpgradeableReadLock和ExitUpgradeableReadLock方法,先获取读锁后读取数据,如发现必须修改,只需要使用EnterWriteLock升级锁,然后快速的进行一次写操作,最后使用ExitWriteLock释放写锁。

    ReaderWriterLockSlim类提供了可升级读模式,这种方式和读模式的区别在于它还有通过调用 EnterWriteLock 或 TryEnterWriteLock 方法升级为写入模式。 因为每次只能有一个线程处于可升级模式。进入可升级模式的线程,不会影响读取模式的线程,即当一个线程进入可升级模式,任意数量线程可以同时进入读取模式,不会阻塞。如果有多个线程已经在等待获取写入锁,那么运行EnterUpgradeableReadLock将会阻塞,直到那些线程超时或者退出写入锁。

9、SpinWait 混合同步线程等待

它是一个混合同步构造,被设计为使用用户模式等待一段时间,然后切换到内核模式以节省CPU时间。

		//初始化
		var w = new SpinWait();	
		//添加一次迭代
		w.SpinOnce();

    我们使用了SpinWait版本,刚开始,SpinWait尝试使用用户模式,在9个迭//代后,开始切换线程为阻塞状态。如果尝试测量该版本的CPU负载,在Windows任务管理器将不会看到任何CPU的使用。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值