线程安全--线程同步

比如有2个 线程同时访问变量 a 同时给他赋值++操作

//共享变量
int count = 0;

 

Thread t1 = new Thread(AddNumber);
Thread t2 = new Thread(AddNumber);
t1.Start();
t2.Start();
//方法用于阻塞调用线程,直到某个线程终止为止。当你想要等待另一个线程完成其任务后再继续执行时,这个方法非常有用。
t1.Join();
t2.Join();

Console.WriteLine( $"{count}");

 

private void AddNumber(){
  for (int i = 0; i < 100000; i++){
     lock (lockobj) {
         count++;
         }
     }
}

 

那么这个时候 就会出现 count 可能 不是200000 ;

这个时候就需要在 count++ 变量赋值的地方 加锁解决

//锁

object lockobj=new object();

private void AddNumber(){
  for (int i = 0; i < 100000; i++){
     lock (lockobj) {

      count++;

  }  
       
         }
     }
}

这个时候 结果就会是200000,不会存在 上面的结果两个线程同时 对一个变量的访问 导致线程安全


什么是线程安全?
      线程安全
                   多个线程访问共享资源时,对共享资源的访问不会导致数据不一致或不可预期的结果
       同步机制
                  用于协调和控制多个线程之间执行顺序和互斥访问共享资源
                  确保线程按照特定的顺序执行,避免竞态条件和数据不一致的问题
        原子操作
                   在执行过程中不会被中断的操作。不可分割,要么完全执行,要么完全不执行,没有中间状态
                   在多线程环境下,原子操作能够保证数据的一致性和可靠性,避免出现竞态条件和数据竞争的问题


C# 1.原子操作类:Lnterlocked 提供 Increment , Decrement 和Add 等基本数学操作的原子方法

  同样是这个例子中

  我们只需要换成

  private void AddNumber(){
      for (int i = 0; i < 100000; i++){

       Lnterlocked .Increment(count)//做自增

      // Lnterlocked .Decrement (count) 做减法

             }
           }
   2.同步类 Mutex 同步两个单独的程序。Mutex 是一种原始的同步方式,其支队一个线程授予对共享资源的独占访问

   

const string MutexName = "CSharpThreadingCookbook";

using (var m = new Mutex(false, MutexName))
{
if (!m.WaitOne(TimeSpan.FromSeconds(5), false))
{
WriteLine("Second instance is running!");
}
else
{
WriteLine("Running!");
ReadLine();
m.ReleaseMutex();
}
}

3.SemaphoreSlim 类是如何作为 Semaphore 类的轻量级版本的。该类限制了同时访问同一个资源的线程数量

      SemaphoreSlim _semaphore = new SemaphoreSlim(4);// 构造函数 参数 就是限制线程访问数量

        _semaphore.Wait(); //阻塞其他线程,知道它进入 SemaphoreSlim 

        _semaphore.Release();//释放一次访问 

4. AutoResetEvent类来从一个线程向另一个线程发送通知。AutoResetEvent类可以通知等待的线程有某事件发生。

    AutoResetEvent workerEvent = new AutoResetEvent(false);

      workerEvent.WaitOne() //任何线程调用 这个waitone()方法将会阻塞 直到调用了set()

      workerEvent.set()//讲时间信号设为有状态,从而允许一个或多个线程继续执行

5.ManualResetEventSlim ,ManualResetEvent 和AutoReseEvent 实质差不多

6.CountdownEvent  信号类来等待知道一定数量的操作完成

static void Main(string[] args)
{
WriteLine("Starting two operations");
var t1 = new Thread(() => PerformOperation("Operation 1 is completed", 4));
var t2 = new Thread(() => PerformOperation("Operation 2 is completed", 8));
t1.Start();
t2.Start();
_countdown.Wait();//当线程信号类调用countdown.Signal()完成之后释放等待(这里面有个大坑就是 信号类里面如果有多个线程 只要有一个没有执行完成 就会导致 调用线程 一直处于阻塞状态)
WriteLine("Both operations have been completed.");
_countdown.Dispose();
}

static CountdownEvent _countdown = new CountdownEvent(2);//构造参数表示2个线程

static void PerformOperation(string message, int seconds)
{
Sleep(TimeSpan.FromSeconds(seconds));
WriteLine(message);
_countdown.Signal();//当线程
}

7.另一种有意思的同步方式,被称为Barrier。Barrier类用于组织多个线程及时在某个时刻碰面。其提供了一个回调函数,每次线程调用了SignalAndWait方法后该回调函数会被执行。

       

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 his song", 2));

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

static Barrier _barrier = new Barrier(2,b => WriteLine($"End of phase {b.CurrentPhaseNumber + 1}"));

static void PlayMusic(string name, string message, int seconds)
{
for (int i = 1; i < 3; i++)
{
WriteLine("----------------------------------------------");
Sleep(TimeSpan.FromSeconds(seconds));
WriteLine($"{name} starts to {message}");
Sleep(TimeSpan.FromSeconds(seconds));
WriteLine($"{name} finishes to {message}");
_barrier.SignalAndWait();
}
}

 

8.ReaderWriterLockSlim来创建一个线程安全的机制,在多线程中对一个集合进行读写操作。ReaderWriterLockSlim 代表了一个管理资源访问的锁,允许多个线程同时读取,以及独占写。(读写锁)

    

 

posted @ 2024-04-22 18:35  jackwu74  阅读(1)  评论(0编辑  收藏  举报