0

0

C#怎么实现多线程安全计数_C#如何使用Interlocked原子操作【指南】

尼克

尼克

发布时间:2026-03-16 13:06:39

|

214人浏览过

|

来源于php中文网

原创

++在多线程中不安全,因其拆分为读值、加1、写回三步非原子操作,导致竞态漏计数;应使用Interlocked.Increment/Add等原子方法,注意适用范围与部署边界。

为什么 ++ 在多线程里不安全

因为 ++ 不是原子操作:它实际拆成「读值 → 加1 → 写回」三步。两个线程同时读到同一个旧值,各自加1再写回,结果只+1而不是+2。这不是偶发bug,是必然发生——只要没同步,就漏计数。

常见错误现象:Task.Run(() => count++).Wait() 跑100次,count 最终远小于100;用 lock 能解决但吞吐低;有人试图用 volatile,但它只保可见性,不保原子性,照样错。

  • 适用场景:高频更新的共享计数器(如请求统计、限流计数、缓存命中计数)
  • 别对非整型变量(如 double、自定义对象)用 Interlocked 原子操作——它只支持 intlongIntPtr 等少数类型
  • 性能影响极小:比 lock 快5–10倍,底层直接映射到 CPU 的 XADDLOCK XCHG 指令

Interlocked.IncrementInterlocked.Add 怎么选

Interlocked.Increment 只能 +1,Interlocked.Add 支持任意整数增减。两者都返回操作后的最新值,这点很重要——你不需要再读一次变量。

示例:

int count = 0;
// 正确:拿到新值,且线程安全
int newValue = Interlocked.Increment(ref count); // 返回 1

// 正确:加5,也返回新值
int afterAdd = Interlocked.Add(ref count, 5); // 返回 6

// 错误:不要这样链式调用,语义不清且无必要
Interlocked.Increment(ref count);
Interlocked.Increment(ref count); // 写两行不如用 Add(ref count, 2)
  • 如果只是累加固定步长(比如每次+1),优先用 Increment / Decrement,语义更直白
  • 如果步长可变(如按请求大小加权计数),必须用 Add
  • 注意 ref 修饰符不能省:Interlocked.Increment(ref count),传值进去会编译报错

读取计数值时要不要加锁或用 Interlocked

单纯读取 int 是原子的(x86/x64 上读写 32 位对齐变量天然原子),但存在可见性问题:一个线程改了值,另一个线程可能还在用寄存器里的旧副本。

所以不建议裸读。有三种安全做法:

课游记AI
课游记AI

AI原生学习产品

下载
  • Volatile.Read(ref count) —— 轻量,强制从内存读,不重排指令
  • Interlocked.CompareExchange(ref count, 0, 0) —— 返回当前值,顺便带内存栅栏,稍重但通用
  • 如果读操作本身就在临界区里(比如和写一起被 lock 包着),那直接读没问题,但这就失去用 Interlocked 的意义了

别用 volatile int count 字段然后直接 count++——volatile 不救原子性,只救可见性。

跨进程或跨 AppDomain 怎么计数

Interlocked 只作用于同一进程内的内存地址,对文件、数据库、Redis、甚至另一个 .NET 进程完全无效。如果你的应用是多实例(如 IIS 多 worker 进程、Kestrel 多实例),Interlocked 对它们之间毫无意义。

这时候得换方案:

  • 用分布式锁 + 数据库自增字段(如 PostgreSQL 的 nextval()
  • 用 Redis 的 INCR 命令——它本身就是服务端原子操作
  • 用内存映射文件(MemoryMappedFile)配合 Interlocked,仅限同一台机器的多个进程,且需手动处理初始化和清理

最容易被忽略的一点:你以为在“多线程”里用 Interlocked 就万事大吉,但其实部署模型早把你带进“多进程”甚至“多机器”的坑里了——先确认你的并发边界在哪,再决定用哪层原子性。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
什么是分布式
什么是分布式

分布式是一种计算和数据处理的方式,将计算任务或数据分散到多个计算机或节点中进行处理。本专题为大家提供分布式相关的文章、下载、课程内容,供大家免费下载体验。

433

2023.08.11

分布式和微服务的区别
分布式和微服务的区别

分布式和微服务的区别在定义和概念、设计思想、粒度和复杂性、服务边界和自治性、技术栈和部署方式等。本专题为大家提供分布式和微服务相关的文章、下载、课程内容,供大家免费下载体验。

252

2023.10.07

counta和count的区别
counta和count的区别

Count函数用于计算指定范围内数字的个数,而CountA函数用于计算指定范围内非空单元格的个数。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

203

2023.11.20

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

1071

2023.08.02

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

617

2024.08.29

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

335

2025.08.29

C++中int的含义
C++中int的含义

本专题整合了C++中int相关内容,阅读专题下面的文章了解更多详细内容。

235

2025.08.29

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

335

2025.08.29

C++多线程并发控制与线程安全设计实践
C++多线程并发控制与线程安全设计实践

本专题围绕 C++ 在高性能系统开发中的并发控制技术展开,系统讲解多线程编程模型与线程安全设计方法。内容包括互斥锁、读写锁、条件变量、原子操作以及线程池实现机制,同时结合实际案例分析并发竞争、死锁避免与性能优化策略。通过实践讲解,帮助开发者掌握构建稳定高效并发系统的关键技术。

2

2026.03.16

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C# 教程
C# 教程

共94课时 | 11.4万人学习

C 教程
C 教程

共75课时 | 5.5万人学习

C++教程
C++教程

共115课时 | 22.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号