c# lock(typeof(MyClass)) 这种写法的风险是什么

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

为什么
lock(typeof(MyClass))
会锁住整个类型而不是实例

因为

typeof(MyClass)
返回的是运行时的
Type
对象,而每个类型在 AppDomain(或 .NET Core/.NET 5+ 的 AssemblyLoadContext)中只有一个全局唯一的
Type
实例。所有对该类型的
lock(typeof(MyClass))
调用,本质上都在争抢同一把锁 —— 即使发生在完全无关的类、模块甚至第三方库中。

它和
lock(this)
private static readonly object _lock = new()
的关键区别

前者是**跨类、跨模块、跨调用栈的隐式全局锁**;后两者作用域明确、可控。常见后果包括:

不同业务逻辑(比如日志初始化和配置加载)意外串行,导致本可并发的操作被阻塞 第三方 NuGet 包若也用了
lock(typeof(MyClass))
(尤其在通用工具类里),你的代码可能被它拖慢,反之亦然
在 ASP.NET Core 等多租户场景下,一个请求卡住
lock(typeof(Startup))
,可能让所有后续请求排队等待
单元测试并行执行时,多个测试用例因共享同一
Type
锁而相互干扰,出现偶发超时或死锁

typeof(MyClass)
锁在 .NET Core / .NET 5+ 中是否更危险

是的。.NET Core 引入了

AssemblyLoadContext
,同一个程序集可能被加载多次(例如插件场景、动态编译)。此时
typeof(MyClass)
在不同上下文中返回的
Type
对象**不相等**,但开发者通常意识不到这点 —— 表面看是“同一个类”,实际锁根本不同,导致本该串行的逻辑变成竞态;或者反过来,误以为安全而没加锁,结果出错。

更隐蔽的问题是:IL 编译器或 AOT(如 NativeAOT)可能对

typeof
做优化,进一步加剧行为不一致。

替代方案:安全又清晰的写法

用显式声明的静态锁对象,确保作用域唯一、意图明确:

public class MyClass
{
    private static readonly object _syncRoot = new();
<pre class='brush:php;toolbar:false;'>public void DoWork()
{
    lock (_syncRoot)
    {
        // 安全的临界区
    }
}

}

如果需要细粒度控制(如按 ID 锁不同资源),改用

ConcurrentDictionary<string object></string>
+
GetOrAdd
,避免锁爆炸:

private static readonly ConcurrentDictionary<string, object> _perKeyLocks 
    = new();
<p>public void DoWorkFor(string key)
{
var lockObj = <em>perKeyLocks.GetOrAdd(key, </em> => new object());
lock (lockObj)
{
// 按 key 隔离,不干扰其他 key
}
}

绝对不要依赖

Type
string
字面量(如
lock("MyClass")
)、或任何可能被外部代码复用的引用对象作锁目标。

真正麻烦的从来不是“要不要加锁”,而是“锁的边界是否恰好覆盖你要保护的资源,且不超出”。

typeof(MyClass)
的边界是不可控的全局,这在现代分层、插件化、测试并行的 C# 工程里,基本等于埋雷。

相关推荐