首页 > 编程语言 >C# 线程同步方法

C# 线程同步方法

时间:2022-08-16 21:18:29浏览次数:83  
标签:同步 Console C# void 信号量 static WriteLine 线程

1、lock

锁定的是一个引用类型,值类型不能被锁定,但应该避免锁定一个string,因为string的存储方式是不样的。

主要注意的就是被锁的这个对象。

class Program
    {
        private static readonly object objLock = new object();
        static int te = 10;
        static int num = 0;
        static void Main(string[] args)
        {
            for (int i = 0; i < 10000; i++)
            {
                Task.Run(Method);
            }

             Thread.Sleep(100);
            Console.WriteLine(num.ToString());
            Console.ReadLine();
        }
        public static void Method()
        {
            lock (objLock)
            {
                num++;
            }
            // num++;
        }
    }

 

2、InterLocked

如果一个变量被多个线程修改,读取。可以用InterLocked

计算机上不能保证对一个数据的增删是原子性的,因为对数据的操作也是分步骤的:

  1.将实例变量中的值加载到寄存器中。

  2.增加或减少该值。

  3.在实例变量中存储该值。

InterLocked为多线程共享的变量提供原子操作。

InterLocked提供了需要原子操作的方法:

    class Program
    {
        public static int value1 = 0; 
        public static int value2 = 0; 
        static void Main(string[] args)
        {
            TestIncrement();
            Thread.Sleep(2000);
            Console.WriteLine(value1+"\n"+value2);
            Console.ReadLine();
        }
        public static void TestIncrement()
        {
            for(int i = 0; i < 5; i++){
                Task.Run(IncrementValue);
            }
        }
        public static void IncrementValue()
        {
            for(int i = 0; i < 1000000; i++)
            {
                value1++;
                Interlocked.Increment(ref value2);
            }
        }
    }

 

3、Moniter

排他锁

lock其实就是Moniter的语法糖,二者其实就是一样的,Moniter可以更灵活就是了。可以使用TryEnter设置最大等待时间

	class Program
	{
		private static readonly object lock1 = new object();
		private static readonly object lock2 = new object();
		static void Main(string[] args)
		{

			Task.Run(LockTooMuch);

			lock (lock2)
			{
				Thread.Sleep(1000);	//这里拿到了lock2,而LockTooMuch拿到了lock1
				Console.WriteLine("Monitor.TryEnter allows not to get stuck, returning false after a specified timeout is elapsed");
				if (Monitor.TryEnter(lock1, TimeSpan.FromSeconds(5)))	//拿不到资源,等5s后就进入else语句了
				{
					Console.WriteLine("Acquired a protected resource succesfully");
				}
				else
				{
					Console.WriteLine("Timeout acquiring a resource!");
				}
			}

			Task.Run(LockTooMuch);

			Console.WriteLine("----------------------------------");
			lock (lock2)	//这里会直接死锁
			{
				Console.WriteLine("This will be a deadlock!");
				Thread.Sleep(1000);
				lock (lock1)
				{
					Console.WriteLine("Acquired a protected resource succesfully");
				}
			}
		}

		static void LockTooMuch()
		{
			lock (lock1)
			{
				Thread.Sleep(1000);
				lock (lock2) { };
			}
		}
	}

 

4、WaitHandle

参考博客:点这里

这个类是一个抽象类,是用来封装等待对共享资源进行独占访问的操作系统特定的对象。

 

5、AutoResetEvent

AutoResetEvent表示线程同步事件在一个等待线程释放后收到信号时自动重置。WaitOne()方法阻塞程序执行,Set()方法释放信息。当释放后阻塞的代码继续执行。但下一次执行还需要等待信号。WaitOne()相当于要进门,但是门关了,所以要等Set()方法来开门,WaitOne()下一次来的时候还得等,因为自动调用了ReSet()又把门关上了。

    class Program
    {
        static int num = 0;
        private static AutoResetEvent ate1 = new AutoResetEvent(false);
        private static AutoResetEvent ate2 = new AutoResetEvent(true);  //设为True表示一开始没有设置Reset(),所以第一个WaitOne()没有效果

        static void Main(string[] args)
        {
            Task.Run(Method1);
            Task.Run(Method2);

            Console.ReadLine();
        }
        public static void Method1()
        {
            while (num < 100)
            {
                ate1.WaitOne();
                Console.WriteLine("奇数:"+num++.ToString());
                ate2.Set(); //这里自动的调用了.Reset()方法
            }
        }
        public static void Method2()
        {
            while (num < 100)
            {
                ate2.WaitOne();
                Console.WriteLine("偶数:" + num++.ToString());
                ate1.Set();
            }
        }
    }

WaitOne还有一些重载,加一个整数参数表示最大等待时长上限,-1就表示无限等了,返回值为bool型,等到了就是True,没等到就是False。

 

6、ManualResetEvent

和AutoResetEvent的区别就和它们的名称一样,一个自动Reset(),一个手动Reset()。其他地方完全一样

    class Program
    {
        static int num = 0;
        private static ManualResetEvent ate1 = new ManualResetEvent(false);
        private static ManualResetEvent ate2 = new ManualResetEvent(true);  //设为True表示一开始没有设置Reset(),所以第一个WaitOne()没有效果

        static void Main(string[] args)
        {
            Task.Run(Method1);
            Task.Run(Method2);

            Console.ReadLine();
        }
        public static void Method1()
        {
            while (num < 100)
            {
                ate1.WaitOne();
                Console.WriteLine("奇数:" + num++.ToString());
                ate2.Set();
                ate2.Reset();
            }
        }
        public static void Method2()
        {
            while (num < 100)
            {
                ate2.WaitOne();
                Console.WriteLine("偶数:" + num++.ToString());
                ate1.Set();
                ate1.Reset();
            }
        }
    }

 

7、Semaphore

其实.NET中的信号量(Semaphore)是操作系统维持的一个整数。当整数位0时。其他线程无法进入。当整数大于0时,线程可以进入。每当一个线程进入,整数-1,线程退出后整数+1。整数不能超过信号量的最大请求数。信号量在初始化的时候可以指定这个整数的初始值。

WaitOne()方法表示当前线程占用信号量,Release() 方法表示当前线程释放信号量

    class Program
    {
        static void Main(string[] args)
        {
            Foo foo = new Foo();

            Console.ReadLine();
        }
    }

    public class Foo
    {
        Semaphore semaphore = new Semaphore(0, 3);//允许最大3个线程占用信号量 超过后就会在WaitOne那阻塞

        public Foo()
        {
            for (int i = 0; i < 10; i++)
            {
                int j = i;
                Task.Run(() => DoSomething(j));
            }
            //semaphore.Release(3);
            //Console.WriteLine(semaphore.Release()); 
            //Console.WriteLine(semaphore.Release()); 
            //Console.WriteLine(semaphore.Release()); 
            //Console.WriteLine(semaphore.Release()); 

        }

        private void DoSomething(int i)
        {
            semaphore.WaitOne();//当前线程占用信号量  如果信号量被占满 则阻塞
            Console.WriteLine($"{i}进入信号量");

            Thread.Sleep(1000);

            Console.WriteLine($"{i}释放信号量  信号量线程数量:{semaphore.Release()}");//当前线程释放信号量
        }
    }

 

8、SemaphoreSlim

SimaphoreSlim是轻量级的,操作和Semapore是一样的

本质区别是 Semaphore 是基于系统提供的同步原语实现的同步等待,而 SemaphoreSlim 是通过自旋(基于 SpinWait) 实现的同步等待。

Slim 版本在大多数情况下不会出现用户态到系统态的转换,而 Semaphore 则非常可能发生这种情况。但由于 SpinWait 的特点,Slim 版本更加适用于等待时间较短的场景。因为如果出现了长时间的等待(长过一次工作状态的切换),SpinWait 会放弃继续空循环的操作,将执行机会让给其他的线程,这样又会导致上下文的切换。

    class Program
    {
        static void Main(string[] args)
        {
            Foo foo = new Foo();

            Console.ReadLine();
        }
    }

    public class Foo
    {
        Semaphore semaphore = new Semaphore(0, 3);//允许最大3个线程占用信号量 超过后就会在WaitOne那阻塞
        SemaphoreSlim semaphoreSlim = new SemaphoreSlim(5);

        public Foo()
        {
            for (int i = 0; i < 10; i++)
            {
                int j = i;
                Task.Run(() => DoSomething(j));
            }
            Thread.Sleep(10000);
            //semaphore.Release();
            //semaphore.Release(3);
            Console.WriteLine(semaphoreSlim.Release()); 
            //Console.WriteLine(semaphore.Release()); 
            //Console.WriteLine(semaphore.Release()); 
            //Console.WriteLine(semaphore.Release()); 

        }

        private void DoSomething(int i)
        {
           // semaphore.WaitOne();//当前线程占用信号量  如果信号量被占满 则阻塞
            semaphoreSlim.Wait();
            Console.WriteLine($"{i}进入信号量");

            Thread.Sleep(1000);

           // Console.WriteLine($"{i}释放信号量  信号量线程数量:{semaphore.Release()}");//当前线程释放信号量
            Console.WriteLine($"{i}释放信号量  信号量线程数量:{semaphoreSlim.Release()}");//当前线程释放信号量
        }
    }

9、Mutex

互斥锁是一个互斥的同步对象,意味着同一时间有且仅有一个线程可以获取它,互斥锁可适用于一个共享资源每次只能被一个线程访问的情况,如果线程获取互斥体,则需要获取该互斥体的第二个线程将挂起,直到第一个线程释放该互斥体。

在Mutex类中,WaitOne()方法用于等待资源被释放,ReleaseMutex()方法用于释放资源。WaitOne()方法在等待ReleaseMutex()方法执行后才会结束。

    class Program
    {
        static Mutex mutex = new Mutex();//互斥锁      
        static bool hasPay = false;
        static void Pay(int num)
        {
            if (mutex.WaitOne())
            {
                try
                {
                    if (!hasPay)
                    {
                        Console.WriteLine($"编号{num}:已经买单了");
                        hasPay = true;
                    }
                    else
                        Console.WriteLine($"编号{num}:买单失败");
                }
                finally
                {
                    mutex.ReleaseMutex();
                    Console.WriteLine("mutex is Release");
                }
            }
        }
        static void Main(string[] args)
        {
            for (int i = 1; i < 10; i++)
            {
                int j = i;
                Task.Factory.StartNew(()=>Pay(j));
            }
            Console.ReadLine();
        }
    }

通过设置Mutex的名称可以做到跨进程,让整个操作系统实现线程同步操作。

标签:同步,Console,C#,void,信号量,static,WriteLine,线程
From: https://www.cnblogs.com/xunzf0402/p/16592331.html

相关文章

  • SpringMvc_03RestuFul风格
    概念:Restful就是一个资源定位以及资源操作的风格,不是标准也不是协议,只是一种风格,基于这个风格的软件可以更简洁,更有层次,更易于实现缓存机制。功能资源:互联网所有的事务......
  • [Typescript] Making TypeScript Stick - 6 - infer, build a ConstructorArg Type
    ForexamplewehaveaWebpackclass:classWebpackCompiler{constructor(options:{amd?:false|{[index:string]:any}bail?:booleancache?:......
  • udhcpc如何绑定网络接口
    udhcpc如何绑定网络接口https://kysonlok.gitbook.io/blog/openwrt/how_does_udhcpc_work IntroduceOpenWrt缺省控制DHCP的进程如下......
  • K8S 四种client简单介绍
    K8S四种client简单介绍client-go支持RESTClient、ClientSet、DynamicClient、DiscoveryClient四种客户端与KubernetesApiServer进行交互RESTClient客户端restclient客......
  • C++primer练习13.55-58
    练习13.55为你的StrBlob添加一个右值引用版本的Push_backvoidStrBlob::push_back(string&&s){data->push_back(std::move(s));}练习13.56如果sorted定义如下,会发生......
  • C#循环语法
    原文:https://www.runoob.com/csharp/csharp-loops.html有的时候,可能需要多次执行同一块代码。一般情况下,语句是顺序执行的:函数中的第一个语句先执行,接着是第二个语句,依此......
  • #C220816C. 时间复杂度
    #C220816C.时间复杂度C220816C校内模拟赛背景注意:本题采用捆绑测试。题目描述在你的帮助下,小凯成功找到了宝藏价值最大的方案。接下来他在闲逛时被一个游戏机吸引了......
  • /opt/module/canal/conf/canal.properties
    ##########################################################commonargument###############################################################tcpbindipcanal.id......
  • /opt/module/canal/conf/example/instance.properties
    ###################################################mysqlserverId,v1.0.26+willautoGencanal.instance.mysql.slaveId=1234canal.instance.gtidon=false#positi......
  • AtCoder Beginner Contest 258
    A-When?问21:00后的第k分钟的时间#include<bits/stdc++.h>usingnamespacestd;constintN=2e5+5;intn,a[N],cnt,k;int32_tmain(){ intn,h=21......