线程基础篇-线程同步

Lock是将一段代码定义为临界区,临界区中的代码在同一时刻只能有一个线程访问,当临界区代码执行时,其他线程调用会被阻塞,需等待资源释放。

语法:

private object locker=new object();

void Method()

{
lock(locker)
{
代码块…
}
}
注意:

1 lock不能锁定空值

2 lock不能string类型,因为字符驻留机制,无法被释放

3 lock不能锁定值类型

4 lock避免锁定public类型或不受程序控制的对象,容易出现死锁
实例
基础类

class CTest
    {
        private bool deadlocked = true;
        private object locker = new object();
        public void LockMe(object o)
        {
            lock(locker)
            {
                while(deadlocked)
                {
                    deadlocked=(bool)o;
                    Console.WriteLine("Foo:I am locked :(");
                    Thread.Sleep(500);
                }
            }
        }
        public void DoNotLockMe()
        {
            Console.WriteLine("I am not locked:)");
        }
    }

操作

       static void Main(string[] args)
        {
            CTest c1 = new CTest();
            Thread th = new Thread(c1.LockMe);
            th.Start(true);
            Thread.Sleep(100);
            lock (c1)
            {
                c1.DoNotLockMe();
                c1.LockMe(false);
            }
            Console.ReadKey();
        }

显示效果:
在这里插入图片描述

Monitor

lcok的底层是Monitor.Enter和Moniter.Exit,有了lock语法糖可以轻松实现加锁操作,为了更精确的操作,需要使用Monitor类

Monitor.Enter 上锁,锁定资源

Monitor.TryEnter 在指定超时时间内锁定资源,可避免死锁

Monitor.Wait 暂时释放资源

Monitor.Pulse 唤醒等待队列中的线程

Monitor.Exit 释放资源
实例:

打印奇偶数方法

	public void PrintEven()
	{
		Monitor.Enter(this);
		try
		{
			for(i=0 ;i<=10;i=i+2)
			{
				Console.WriteLine(Thread.CurrentThread.Name+"-----"+i);
			}
		}
		Catch(Exception)
		{
			throw;
		}
		finally
		{
			Monitor.Exit(this);
		}
	}
	public void PrintOdd()
	{
		Monitor.Enter(this);
		try
		{
			for(i=1;i<=10;i=i+2)
			{
				Console.WriteLine(Thread.CurrentThread.Name+"----"+i);
			}
			catch(Exception)
			{
				throw;
			}
			finally
			{
				Monitor.Exit(this);
			}
		}
	}

操作

	static void Main(string[] args)
	{
		Program p = new Program();
		Thread the = new Thread(p.PrintEven);
		the.IsBackground=true;
		the.Name="打印偶数";
		the.Start();
		Thread tho = new Thread(p.PrintOdd);
		tho.IsBackground=true;
		tho.Name="打印奇数";
		tho.Start();

		Console.ReadKey();

	}

显示效果:
在这里插入图片描述

ReaderWriterLock

主要解决类似数据库这种读取数据多写入数据少的情况,如用Monitor类则形成独占,不能实现多个线程读取数据,读写锁很好的解决了这一情况

主要包括以下几个方法:

AcquireReaderLock 获取读取锁

ReleaseReaderLock 释放读取锁

AcquireWriterLock 获取写入锁

ReleaseWriterLock 释放写入锁

UpgradeToWriterLock 读锁转换为写锁

DowngradeFromWriterLock 写锁转换为读锁

Mutex

跨多个线程同步访问的类,只有一个线程能获得互斥锁定,访问受互斥保护的同步代码区域。

WaitOne 等待资源释放

ReleaseMutex 释放资源

最常用的是程序启动时判断是否已有实例在运行
代码如下:

       static void Main()
        {
            bool flag = false;
            bool requestInitialOwnership = true;
            Mutex mt = new Mutex(requestInitialOwnership, "MutexTest1", out flag);
            if (flag)
            {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                Application.Run(new Form1());
                
            }
            else
            {
                MessageBox.Show("MutexTest1已启动");
                Application.Exit();
            }
            
        }

显示效果:
在这里插入图片描述
排队买票,同一窗口在同一时间只能有一个人买票
买票方法

   private static Mutex mt = new Mutex();
   public static void BuyTickets(Object name)
   {
   	if(mt.WaitOne())
   	{
   		try
   		{
   			Console.WriteLine("{0}占用窗口并开始买票",name);
   			Thread.Sleep(1000);
   		}
   		catch(Exception)
   		{
   			throw}
   		finally
   		{
   			Console.WriteLine("{0}离开窗口",name);
   			mt.ReleaseMutex();
   		}
   	}
   }

操作

	static void Main()
	{
		Thread th1 = new Thread(BuyTickets);
		th1.Start("小明");
		Thread th2 = new Thread(BuyTickets);
		th2.Start("小A");
		Console.ReadKey();
	}

显示效果:
在这里插入图片描述

Interlocked

为多线程共享整数变量提供原子操作,类似操作系统的PV操作

Interlocked.Read 读取计数器的值

Interlocked.Add 使计数器增加指定的值

Interlocked.Increment 使计数器加一

Interlocked.Decrement 使计数器减一

Interlocked.Exchange 把计数器设定为某个指定值

Interlocked.CompareExchange 将计数器与某个值比较,若相等则计数器设定为指定的值

实例

 	private static char buffer;
        private static long used = 0;
        static void Main(string[] args)
        {
            string str = "壬戌之秋,七月既望,苏子与客泛舟游于赤壁之下。清风徐来,水波不兴。举酒属客,诵明月之诗,歌窈窕之章。";
            Thread thWriter=new Thread(delegate()
            {
                for (int i = 0; i < str.Length; i++)
                {
                    while (Interlocked.Read(ref used) == 1)
                    {
                        Thread.Sleep(50);
                    }
                    buffer = str[i];
                    Interlocked.Increment(ref used);
                }
               
            });
            Thread thReader = new Thread(delegate ()
              {
                  for (int i = 0; i < str.Length; i++)
                  {
                      while (Interlocked.Read(ref used) == 0)
                      {
                          Thread.Sleep(50);
                      }
                      char ch = buffer;
                      Console.Write(ch);
                      Interlocked.Decrement(ref used);
                  }
              });
            thWriter.Start();
            thReader.Start();
            Console.ReadKey();
        }

显示效果:文字会一个一个的打印出来,并且不会乱。
在这里插入图片描述

AutoResetEvent ManualResetEvent

实现线程通信,类似信号量Semaphore

Set() 将信号状态设置为有信号

Reset() 将信号状态设置为无信号

WaitOne() 无信号时线程阻塞,有信号时线程无阻塞

AutoResetEvent和ManualResetEvent的区别在于WaitOne方法,AutoResetEvent的WaitOne会自动改变事件对象的状态,每次只能唤醒一个线程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值