线程安全--线程同步
比如有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 代表了一个管理资源访问的锁,允许多个线程同时读取,以及独占写。(读写锁)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)