笔记:c# 线程同步
作用:避免死锁的同时防止出现竞态条件
方式:
1,monitor 监视者模式,Monitor.Enter 和 Monitor.Exit方法
2,lock关键字
monitor和lock 都要求私有自读变量
注意以上两种方式都要使用到一个变量,这个变量必须是引用类型,因为值类型的时候,运行时会将其进行装箱,每次装箱都是一个新的对象,就让锁失效了。
避免使用this,typeof,string 类型的变量。
不使用string类型变量的原因: 考虑到字符串留用的问题。如果同一个字符串常量在多个位置出现,那么所有位置可能引用同一个实例,使锁定的范围大于预期。
不使用this或typeof 的原因是 :虽然只有实例自身内部的代码能用this关键字来阻塞,但创建实例的调用者仍可将那个实例传给一个同步锁。结果就是对两套不同的数据进行同步的两个同步块可能互相阻塞,极端情况下会造成死锁。
3,将字段声明为 volatile
4,system.threading.Interlocked 类,这个类里的方法都是线程安全的
Exchange方法,赋值的方法
Decrement --方法
Increment ++ 方法
同步的引入带来了死锁的可能
死锁:两个或多个线程都在等待对方释放一个同步锁,就好发生死锁。比如:线程1请求 _Sync1 的锁,并在释放 _Sync1 锁之前,请求 _Sync2 的锁。与此同时,线程2 请求 _Sync2 的锁,并在释放 _Sync2 锁之前,请求 _Sync1 锁。这就极大可能存在死锁。
死锁的发生需要以下几个条件:
1,排他或互斥:一个线程独占一个资源,没有其他线程能获取相同的资源
2,占有并等待:一个排他的线程请求获取另一个线程占有的资源
3,不可抢先:一个线程占有的资源不能被强制拿走,只能等待线程主动释放
4,循环等待条件:两个或多个线程构成一个循环等待链,他们锁定两个多多个相同的资源,每个线程都在等待链中下一个线程占有的资源
死锁的另一个重要原因就是 有些锁是不可重入的。比如说一个线程获得了一个锁,然后重新请求同一个锁,但锁已经被当前线程自己拥有而造成阻塞,那么这个锁就是不可重入的,额外的请请求就会造成死锁。
lock关键字(底层是Monitor类) 生成的代码时可重入的。
更多同步类型:
6,ManualResetEvent 和 AutoResetEvent: 控制多个线程执行顺序
wait() 和 set() \
7,SemaphoreSlim 和 CountdownEvent。 信号量
SemaphoreSlim : 限制只有n个调用能访问一块儿资源,本质上是保持了对资源池的计数。计数为0,就拒绝访问,直到其中一个资源返回,就再放一个请求进来
CountdownEvent: 与之相反。假设一个并行操作,有n个请求,只有全部请求完之后,才会继续执行。
8,线程本地储存
没有使用同步锁,而是使用隔离的手段,将每个线程都有专属的变量实例
(1), threadLocal<T>
(2),ThreadStaticAttribute 这是个属性,用于修饰字段。有个缺点:只有和” 正在运行静态构造函数的线程“ 相关联的变量实例才会被正确的初始化值。 其他线程只能被初始化变量类型的默认值。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构