"b" })只是在做值匹配和分支计算,它不读写共享状态,也不触发副作用。只要表达式里用到的变量是局部的、不可变的,或只读取线程安全的对象(如 string、int、ImmutableArray),那这个表达式在多线程下天然安全。真正出问题的地方往往藏">

c# C# 8 的 switch 表达式和多线程安全

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

switch 表达式本身不涉及线程安全问题

你写

switch
表达式(比如
value switch { 1 => "a", _ => "b" }
)只是在做值匹配和分支计算,它不读写共享状态,也不触发副作用。只要表达式里用到的变量是局部的、不可变的,或只读取线程安全的对象(如
string
int
ImmutableArray<t></t>
),那这个表达式在多线程下天然安全。

真正出问题的地方往往藏在分支右侧的表达式中:

调用非线程安全的方法(如操作
Dictionary<k></k>
List<t></t>
实例)
访问未加锁的静态字段或实例字段 使用
async
lambda 却没处理上下文切换(比如在
Task.Run
中捕获了 UI 线程同步上下文)

在 lock 块或 Concurrent 集合中使用 switch 表达式

如果你必须在分支里修改共享数据,得把临界区控制好——

switch
表达式不能替代同步机制。常见做法是把整个读-改-写逻辑包进
lock
,或改用线程安全集合。

例如,用

ConcurrentDictionary<string int></string>
替代普通字典,再配合
switch
做键映射:

var counter = new ConcurrentDictionary<string, int>();
string key = eventType switch
{
    "click" => "ui_clicks",
    "submit" => "form_submits",
    _ => "other_events"
};
counter.AddOrUpdate(key, 1, (_, c) => c + 1);

注意:

AddOrUpdate
是线程安全的,但
switch
只负责生成
key
,不参与并发控制。

避免在 switch 表达式中直接调用异步方法

C# 8 的

switch
表达式返回的是一个值,不是
Task
;你不能在分支中直接写
await SomeAsync()
。如果强行混合,会遇到编译错误
CS4032: An async lambda expression may only appear inside an async method

正确做法是:先用

switch
确定策略或参数,再在外面驱动异步流程:

string action = operation switch
{
    "read" => "SELECT * FROM users",
    "write" => "INSERT INTO users VALUES (@p1)",
    _ => throw new NotSupportedException()
};
<p>await ExecuteSqlAsync(action, parameters);

别把

await
塞进
=>
右边,那是语法禁区。

模式匹配 + var 声明可能引发意外的引用捕获

当 switch 表达式里用到

var
模式(如
obj switch { var x when x is string => x.Length, ... }
),要注意
x
是对原对象的引用。如果
obj
是跨线程共享的可变引用类型(比如自定义类实例),而你在分支里修改了
x.SomeProperty
,那就引入了竞态条件——这和
switch
无关,但容易被忽略。

建议:

优先用解构或只读属性匹配(
obj is Person { Age: >= 18 }
若需赋值,确保目标对象是线程本地的,或该类型本身是不可变的(如
record
避免在
switch
分支里对传入的引用类型做突变操作

最常被低估的,其实是分支里那个看似无害的

var x
—— 它不复制对象,只传递引用。

相关推荐