c# 在高并发下使用静态变量的注意事项

来源:这里教程网 时间:2026-02-21 17:37:44 作者:

静态变量在多线程环境里不是线程安全的

静态变量属于类型级别,所有线程共享同一份内存。如果多个线程同时读写同一个

static
字段(比如
static int counter
),不加同步就可能丢失更新、读到脏值,甚至触发不可预测的行为。

常见错误现象包括:计数器增长慢于预期、对象状态错乱、

NullReferenceException
(尤其在静态初始化未完成时被其他线程访问)。

不要直接对
static
字段做
++
--
、赋值等非原子操作
避免在静态构造函数中执行耗时或依赖外部资源的操作(如数据库连接、HTTP 调用),它只执行一次且阻塞所有后续线程 静态字段若引用可变对象(如
static List<string> Items</string>
),需确保该对象本身线程安全,或统一加锁访问

Interlocked
替代简单算术操作

对整型、引用类型等基础操作,

Interlocked
提供无锁、原子的线程安全方法,性能远高于
lock
,适合高频计数、标志位切换等场景。

例如:

Interlocked.Increment(ref counter)
lock(obj) { counter++; }
更轻量;
Interlocked.CompareExchange
可实现乐观并发控制。

Interlocked.Add
Interlocked.Exchange
Interlocked.CompareExchange
是常用组合
注意:仅适用于支持的类型(
int
long
IntPtr
、引用类型等),不能用于
decimal
或自定义结构
不要试图用
Interlocked
保护多步逻辑(如“先读再写”),它只保证单个操作原子性

静态集合要用线程安全版本或显式同步

.NET 提供了

ConcurrentDictionary<k></k>
ConcurrentQueue<t></t>
ConcurrentStack<t></t>
等开箱即用的线程安全集合。它们内部采用细粒度锁或无锁算法,比手动
lock
更高效、更不易出错。

如果你坚持用

static List<t></t>
static Dictionary<k></k>
,就必须为每个读写入口显式加锁,且锁对象必须是私有静态字段(不能是
this
或公共实例),否则会锁不住。

优先选
ConcurrentDictionary
而非
static Dictionary
+
lock
ConcurrentBag<t></t>
适合高并发生产者-消费者场景,但不保证遍历顺序
即使用了线程安全集合,复合操作(如“检查是否存在再添加”)仍需额外同步,因为
ContainsKey
+
TryAdd
不是原子的

静态缓存要小心生命周期和内存泄漏

静态变量生命周期贯穿整个 AppDomain(.NET Framework)或进程(.NET Core/5+),不会被 GC 回收。如果静态字段持有大量数据(如缓存了成千上万的

byte[]
或未释放的
IDisposable
对象),极易导致内存持续增长、OOM。

典型问题:用

static Dictionary<string object></string>
做缓存但没设过期策略或大小限制;缓存了事件处理器却忘了反订阅,造成对象无法释放。

考虑用
MemoryCache.Default
IMemoryCache
(ASP.NET Core)替代裸静态字典,它支持过期、大小限制、回调清理
若必须用静态集合,定期清理或使用弱引用(
WeakReference<t></t>
)缓存大对象
静态字段引用的
IDisposable
实例(如
static FileStream
)需在程序退出前显式释放,否则资源泄露
public static class CounterService
{
    private static long _hitCount = 0;
    private static readonly ConcurrentDictionary<string, int> _cache = new();
    public static long IncrementHit() => Interlocked.Increment(ref _hitCount);
    public static bool TryGetCachedValue(string key, out int value) =>
        _cache.TryGetValue(key, out value);
    public static void SetCachedValue(string key, int value) =>
        _cache[key] = value; // ConcurrentDictionary 自动线程安全
}
静态变量不是不能用,而是每处读写都要问一句:这个操作是否会被多个线程同时触发?有没有隐式依赖顺序?它的生命周期是否可控?漏掉其中任何一个,都可能在高并发下突然暴露。

相关推荐