当前位置: 首页 > news >正文

C# 线程同步(一)同步概念介绍

目录

1.阻塞(Blocking)

2.阻塞 VS 轮询

3.线程状态


        到目前为止,我们已经阐述了如何在线程上启动任务、配置线程以及实现双向数据传递。同时,我们也说明了局部变量是线程私有的,而引用可以通过共享字段在线程间传递以实现通信。

下一步的关键是同步机制:通过协调线程行为来获得可预测的结果。当多个线程访问同一数据时,同步显得尤为重要——这个领域看似简单却暗藏风险。

同步构造可分为四大类别:

  1. 简单阻塞方法
    这类方法通过等待其他线程结束或计时完成来实现同步。例如:SleepJoin 和 Task.Wait 都属于简单阻塞方法。

  2. 锁定构造
    用于限制同时执行某段代码或操作的线程数量。最常见的独占锁(如 lock/Monitor.Enter/Monitor.ExitMutex 和 SpinLock)仅允许一个线程进入,确保竞争线程访问共享数据时互不干扰。非独占锁包括信号量(Semaphore/SemaphoreSlim)和读写锁。

  3. 信号构造
    允许线程暂停运行直至接收到其他线程的通知,从而避免低效的轮询。常用信号机制有两种:事件等待句柄(event wait handles)和 Monitor 的 Wait/Pulse 方法。.NET Framework 4.0 新增了 CountdownEvent 和 Barrier 类。

  4. 非阻塞同步构造
    通过调用处理器原语来保护共享字段的访问。CLR 和 C# 提供的非阻塞构造包括:Thread.MemoryBarrierThread.VolatileReadThread.VolatileWritevolatile 关键字以及 Interlocked 类。

除最后一类外,阻塞机制在其他类别中均占核心地位。下面我们将简要探讨这一概念。

1.阻塞(Blocking)

        当线程因某些原因暂停执行时(例如通过 Sleep 进入休眠,或通过 Join/EndInvoke 等待其他线程结束),该线程即被视为阻塞状态。阻塞线程会立即释放其处理器时间片,此后在阻塞条件满足前不再消耗任何处理器资源。可通过线程的 ThreadState 属性检测阻塞状态:

bool blocked = (someThread.ThreadState & ThreadState.WaitSleepJoin) != 0;
(由于线程状态可能在检测与后续操作之间发生变化,此代码仅适用于诊断场景。)

当线程阻塞或解除阻塞时,操作系统会执行上下文切换。这将产生几微秒的开销。

线程会通过以下四种方式解除阻塞(当然,按电脑电源键不算!):

  1. 阻塞条件得到满足

  2. 操作超时(如果指定了超时时间)

  3. 通过Thread.Interrupt被中断

  4. 通过Thread.Abort被中止

需要注意的是,如果线程是通过(已弃用的)Suspend方法暂停执行的,则不会被判定为处于阻塞状态。

2.阻塞 VS 轮询

当线程需要暂停直到特定条件满足时,通常有两种实现方式:

  1. 阻塞等待(高效方式)
    通过信号和锁机制实现,线程会进入阻塞状态直到条件满足,此时操作系统会调度其他线程执行。

  2. 轮询等待(简单但低效)
    线程通过循环检测条件来实现等待,例如:

while (!proceed);  // 忙等待

while (DateTime.Now < nextStartTime); // 时间等待

性能考量:

  • 纯轮询会完全占用CPU资源,因为CLR和操作系统会认为线程正在进行重要计算

  • 这种方式的CPU利用率是100%,极其浪费系统资源

改进方案:
可以采用混合阻塞的方式:

while (!proceed) Thread.Sleep(10);  // 每次检查后休眠10ms

虽然不够优雅,但相比纯轮询能显著降低CPU占用率。不过需要注意共享变量(如proceed标志)的并发访问问题,正确的锁和信号量使用可以避免这些问题。

适用场景:
当预期条件能在极短时间内(如几微秒)满足时,短暂轮询可能更高效,因为它避免了上下文切换的开销和延迟。.NET框架为此提供了专门的工具类和方法,这些内容将在并行编程章节详细介绍。

这里小节一下:

  • 阻塞等待:适合大多数情况,资源利用率高

  • 纯轮询:简单但资源浪费严重

  • 混合模式:折中方案,需要处理并发问题

  • 微秒级等待:特殊场景下短暂轮询可能更优

3.线程状态

        您可以通过ThreadState属性查询线程的执行状态。该属性返回一个ThreadState类型的标志枚举,它以位运算方式组合了三个"层次"的数据。不过,大多数枚举值都是冗余的、未使用的或已废弃的。下图展示了其中一个"层次":

以下代码可将ThreadState精简为四个最常用的状态值:未启动(Unstarted)、运行中(Running)、等待休眠或加入(WaitSleepJoin)和已停止(Stopped):

public static ThreadState SimpleThreadState(ThreadState ts)
{return ts & (ThreadState.Unstarted |ThreadState.Running |ThreadState.WaitSleepJoin |ThreadState.Stopped);
}

ThreadState属性适用于诊断目的,但不适合用于同步控制,因为在检测线程状态和基于该状态执行操作之间,线程状态可能会发生变化。


本节完,下一节将开始介绍同步工具

http://www.dtcms.com/a/265928.html

相关文章:

  • 基于Anything LLM的本地知识库系统远程访问实现路径
  • react-打包和本地预览 ——打包优化
  • 基于CNN的人脸关键点检测
  • 强实时运动控制内核MotionRT750(一):驱动安装、内核配置与使用
  • 【科普】Cygwin与wsl与ssh连接ubuntu有什么区别?DIY机器人工房
  • 【大模型学习】项目练习:文档对话助手
  • 零碳园区如何建设,微电网系统来助力
  • 离线迁移 Conda 环境到 Windows 服务器:用 conda-pack 摆脱硬路径限制
  • 拉横幅横幅识别检测数据集VOC+YOLO格式1962张1类别
  • WAIC 2025预告 | 网易灵动发布+展览,两大「全球首发」即将亮相
  • AS32S601 芯片在卫星互联网推进系统中的技术适配性研究
  • Linux下基于C++11的socket网络编程(Epoll)个人总结版
  • ssh挂载拷贝
  • Java 大视界 -- Java 大数据机器学习模型在自然语言处理中的跨语言信息检索与知识融合(331)
  • 汽车ECU产线烧录和检测软件怎么做?
  • 云计算中的tap口、bond口、qr口:它们究竟有何玄机?
  • 【JS笔记】JS 和 noodjs 的常见操作(十)
  • 数据库10:MySQL的数据类型与约束和属性设置,数据模式
  • EXCEL 基础函数
  • JavaScript的初步学习
  • 未来之窗冥界调试工具—东方仙盟
  • java分页插件| MyBatis-Plus分页 vs PageHelper分页:全面对比与最佳实践
  • 【Bug Recod】更新中...
  • 可执行脚本
  • 08-three.js Textures
  • day15——Java常用API(二):常见算法、正则表达式与异常处理详解
  • 【机器学习深度学习】AI 项目开发流程:从需求到部署的五大阶段
  • Springboot3整合ehcache3缓存--XML配置和编程式配置
  • 移除 Java 列表中的所有空值
  • 一天两道力扣(1)