C#中类似Interlocked.Increment的线程安全可回收序列计数器问询
线程安全可复用顺序计数器实现
嘿,刚好碰到过类似的需求!C#标准库并没有内置完全匹配你描述的组件——也就是那种既能按顺序生成新序号,又能回收已用序号并优先复用(放回队列顶端就优先拿)的线程安全计数器。不过咱们基于.NET自带的并发工具,很快就能实现一个符合要求的版本。
核心思路
- 用你熟悉的
Interlocked.Increment来保证新序号的线程安全生成,确保多线程下不会出现重复的新序号; - 用
ConcurrentStack<long>存储回收的序号——因为你要求归还的序号放回队列顶端,下次优先获取,栈的后进先出特性正好完美匹配这个逻辑; - 每次拿序号时,先尝试从栈里取回收的,没有的话再生成新的。
完整代码实现
using System.Collections.Concurrent; public class ReusableSequentialCounter { // 记录下一个要生成的新序号初始值 private long _nextNewId = 0; // 存储回收的序号,线程安全栈 private readonly ConcurrentStack<long> _recycledIds = new ConcurrentStack<long>(); public long GetNextId() { // 先尝试复用回收的序号,栈顶的先拿 if (_recycledIds.TryPop(out long recycledId)) { return recycledId; } // 没有回收的就生成新序号,减1是让第一个序号从0开始,和你示例一致 return Interlocked.Increment(ref _nextNewId) - 1; } public void ReturnId(long id) { // 把归还的序号压入栈顶,下次获取时会优先拿到它 _recycledIds.Push(id); } }
验证你的示例场景
咱们来跑一遍你说的流程:
- 连续调用
GetNextId(),会依次拿到0,1,2,3,4,5; - 调用
ReturnId(4),再调用ReturnId(1)——此时栈里的顺序是[1,4](因为Push是往栈顶加,所以后Push的1在最上面); - 再次调用
GetNextId(),会先拿到1,再拿4,之后再调用就会生成6,7...,完全和你预期的一致!
线程安全说明
ConcurrentStack本身就是为并发场景设计的,TryPop和Push都是线程安全的原子操作,不需要额外加锁;Interlocked.Increment是系统级的原子操作,保证多线程下新序号不会重复;- 整个实现没有显式锁,性能表现也不错,适合高并发的场景。
如果有特殊需求(比如要限制回收序号的范围、或者需要持久化回收的序号),还可以在这个基础上轻松扩展~
内容的提问来源于stack exchange,提问作者tval
相关产品推荐
相关产品推荐

