C# 线程同步的方法

作者:冬冬他哥哥 时间:2022-12-11 01:46:05 

一、进程内部的线程同步

1、使用lock,用法如下:


private static readonly object SeqLock = new object();

private void Print()
   {
     lock (SeqLock)
     {
       Console.WriteLine("test");
     }
   }

特性:只能传递对象,无法设置等待超时

2、使用:InterLocked(原子操作)

其在System.Threading命名空间下,Interlocked实际是类控制计数器,从而实现进程的同步,其很容易实现生产者消费者模型


//缓冲区,只能容纳一个字符
  private static char buffer;
  //标识量(缓冲区中已使用的空间,初始值为0)
  private static long numberOfUsedSpace = 0;
  static void Main(string[] args)
  {
   //线程:写入者
   Thread Writer = new Thread(delegate ()
   {
    string str = "这里面的字会一个一个读取出来,一个都不会少,,,";
    for (int i = 0; i < 24; i++)
    {
     //写入数据前检查缓冲区是否已满
     //如果已满,就进行等待,直到缓冲区中的数据被进程Reader读取为止
     while (Interlocked.Read(ref numberOfUsedSpace) == 1)
     {
      Thread.Sleep(50);
     }
     buffer = str[i]; //向缓冲区写入数据
     //写入数据后把缓冲区标记为满(由0变为1)
     Interlocked.Increment(ref numberOfUsedSpace);
    }
   });
   //线程:读出者
   Thread Reader = new Thread(delegate ()
   {
    for (int i = 0; i < 24; i++)
    {
     //读取数据前检查缓冲区是否为空
     //如果为空,就进行等待,直到进程Writer向缓冲区中写入数据为止
     while (Interlocked.Read(ref numberOfUsedSpace) == 0)
     {
      Thread.Sleep(50);
     }
     char ch = buffer;  //从缓冲区读取数据
     Console.Write(ch);
     Interlocked.Decrement(ref numberOfUsedSpace);
    }
   });
   //启动线程
   Writer.Start();
   Reader.Start();
   Console.ReadKey();

3、使用Monitor

其中Monitor.Enter()和lock相同


     Monitor.Enter(obj){
       //Synchronized part
     }finally{
       Monitor.Exit(obj);
     }

TryEnter则可设置等待时间等


bool lockTaken=false;
     Monitor.TryEnter(obj, 500, ref lockTaken);
     if(lockTaken){
       try
       {
         //Synchronized part
       }
       finally
       {
         Monitor.Exit(obj);
       }
     }else{
       //don't aquire the lock, excute other parts
     }

二、进程间的同步

1. WaitHandle:

封装等待对共享资源进行独占访问的操作系统特定的对象。 WaitHandle:是一个抽象类,我们一般不直接用,而是用它的派生类:

AutoResetEvent、EventWaitHandle、ManualResetEvent、Mutex、Semaphore

这个抽象类的方法如下:

WaitOne(): 等待一个信号的出现,可设置超时;

WaitAll(): 等待多个信号的出现,可设置超时;

WaitAny(): 等待任意一个信号的出现,可设置超时;

2、Mutex: 与Monitor 类似,只有一个线程能够获取锁定。利用WaitOne() 获取锁定,利用ReleaseMutex() 解除锁定。构造函数使用如下:


     bool isNew = false;
     mutex = new Mutex(false, "Mutex1", out isNew);

参数1:锁创建后是否由主调线程拥有。 如果设为true,相当于调用了WaitOne(),需要释放,否则其他线程无法获取锁;

参数2:锁名称,可通过OpenExist()或TryOpenExist() 打开已有锁,因为操作系统识别有名称的互锁,所以可由不同的进程共享。若锁名称为空,就是未命名的互锁,不能在多个进程之间共享;

参数3:  是否为新创建的互锁;

下面的例子演示Mutex 在进程之间的使用:    class Program


private static Mutex mutex = null;
   static void Main(string[] args)
   {
     bool isNew = false;
     mutex = new Mutex(false, "Mutex1", out isNew);
     Console.WriteLine("Main Start....");
     mutex.WaitOne();
     Console.WriteLine("Aquire Lock and Running....");
     Thread.Sleep(10000);
     mutex.ReleaseMutex();
     Console.WriteLine("Release Lock....");
     Console.WriteLine("Main end....");
     Console.ReadLine();
   }
 }

连续2次运行这个控制台程序的exe,结果如下,首先运行的获取 Mutex1 互锁, 后面运行的会等待直到前面运行的释放 Mutex1 互锁。

 3.Semaphore: 信号量的作用于互斥锁类似,但它可以定义一定数量的线程同时使用。下面是构造函数:


     bool isNew = false;
     semaphore = new Semaphore(3, 3, "semaphore1", out isNew);

参数1:创建后,最初释放的锁的数量,如参数1设为2,参数2设为3,则创建后只有2个锁可用,另1个已经锁定;

参数2:定义可用锁的数量;

参数3:  信号量的名称,与Mutex类似;

参数4:是否为新创建的互锁;

以下例子创建了信号量“semaphore1”,利用Parallel.For() 同步运行Func1() ,在Func1() 中,当线程获取信号量锁,释放锁或等待超时,都会在控制台里输出,


class Program
 {
   private static Semaphore semaphore = null;
   static void Main(string[] args)
   {

Console.WriteLine("Main Start....");
     bool isNew = false;
     semaphore = new Semaphore(3, 3, "semaphore1", out isNew);
     Parallel.For(0, 6, Func1);
     Console.WriteLine("Main end....");
     Console.ReadLine();
   }

static void Func1(int index)
   {
     Console.WriteLine("Task {0} Start....",Task.CurrentId);
     bool isComplete = false;
     while (!isComplete)
     {
       if (semaphore.WaitOne(1000))  
       {
         try
         {
           Console.WriteLine("Task {0} aquire lock....", Task.CurrentId);
           Thread.Sleep(5000);
         }
         finally
         {
           semaphore.Release();
           Console.WriteLine("Task {0} release lock....", Task.CurrentId);
           isComplete = true;
         }
       }
       else
       {
         Console.WriteLine("Task {0} timeout....", Task.CurrentId);
       }
     }
   }

运行结果如下,线程1,2,3首先获取信号量锁,线程4,5,6在等待,直到1,2,3释放,

4. AutoResetEvent 类:

可以使用事件通知其他任务,构造函数为 public AutoResetEvent(bool initialState)。

当initialState=true,处于signaled 模式(终止状态),调用waitone() 也不会阻塞任务,等待信号,调用Reset()方法,可以设置为non-signaled 模式;

当initialState=fasle,处于non-signaled 模式(非终止状态),调用waitone() 会等待信号阻塞当前线程(可以在多个线程中调用,同时阻塞多个线程),直到调用set()发送信号释放线程(调用一次,只能释放一个线程),一般使用这种方式;

以下例子创建5个任务,分别调用waitone()阻塞线程,接着每隔2s 调用set(),


private static AutoResetEvent autoReset = new AutoResetEvent(false);
   static void Main(string[] args)
   {
     Console.WriteLine("Main Start....");
     for (int i = 0; i < 5; i++)
     {
       Task.Factory.StartNew(() =>
       {
         Console.WriteLine("{0} Start....", Task.CurrentId);
         autoReset.WaitOne();
         Console.WriteLine("{0} Continue....", Task.CurrentId);
       });
     }
     for (int i = 0; i < 5;i++ )
     {
       Thread.Sleep(2000);
       autoReset.Set();
     }
     Console.WriteLine("Main end....");
     Console.ReadLine();
   }

运行结果每次顺序略有不同,释放是随机的:

5. ManualResetEvent 类:功能基本上和AutoSetEvent类似,但又一个不同点:

使用AutoSetEvent,每次调用set(),切换到终止模式,只能释放一个waitone(),便会自动切换到非终止模式;但ManualResetEvent,调用set(),切换到终止模式,可以释放当前所有的waitone(),需要手动调用reset()才能切换到非终止模式。

以下例子说明了这个不同的:


private static ManualResetEvent manualReset = new ManualResetEvent(false);
   static void Main(string[] args)
   {
     Console.WriteLine("Main Start....");
     for (int i = 0; i < 5; i++)
     {
       Task.Factory.StartNew(() =>
       {
         Console.WriteLine("{0} Start....", Task.CurrentId);
         manualReset.WaitOne();
         Console.WriteLine("{0} Continue....", Task.CurrentId);
       });
     }
     Thread.Sleep(2000);
     manualReset.Set();
     manualReset.WaitOne();
     Console.WriteLine("it doesn't work now, Main continue....");
     manualReset.Reset();
     manualReset.WaitOne();
     Console.WriteLine("Main end....");
     Console.ReadLine();
   }

来源:https://www.cnblogs.com/xietianjiao/p/13386373.html

标签:c#,线程,同步
0
投稿

猜你喜欢

  • C#集合之集(set)的用法

    2023-05-18 06:04:53
  • JavaWeb中JavaMail创建邮件和发送邮件

    2022-01-29 02:54:09
  • 用JAVA 设计生成二维码详细教程

    2021-05-29 05:48:29
  • Java 集合中的类关于线程安全

    2023-03-13 12:53:22
  • 为什么mybatis中的SqlSession一定要关闭

    2022-02-24 07:57:11
  • Android 7.0 Nougat不得不知的11项新功能

    2021-06-12 22:50:07
  • java IO流 之 输出流 OutputString()的使用

    2023-08-11 23:16:30
  • Android自定义覆盖层控件 悬浮窗控件

    2021-10-21 01:14:40
  • 详解C# 结构体

    2023-09-30 15:37:19
  • Mybatis的特点及优点

    2022-11-19 16:27:54
  • mybatis中的count()按条件查询方式

    2022-06-30 06:04:29
  • WindowsForm实现警告消息框的实例代码

    2023-05-25 00:00:54
  • flutter实现底部不规则导航栏

    2023-03-03 04:33:08
  • C#统计字符串的方法

    2021-06-12 17:00:12
  • Mybatis环境配置及测试详解

    2023-11-24 07:06:25
  • java数据结构排序算法之树形选择排序详解

    2022-07-22 23:43:17
  • SpringBoot参数校验Validator框架详解

    2023-09-22 07:08:40
  • c# 判断指定文件是否存在的简单实现

    2023-10-16 01:39:54
  • Java中集合和数组的排序方式小结

    2023-08-25 00:52:10
  • Android限时抢购倒计时实现代码

    2021-08-09 13:56:51
  • asp之家 软件编程 m.aspxhome.com