C#锁定单行(if语句)

10
这段代码只在if语句上锁定,还是两行代码都会锁定?
lock (LockObject)
    if (instance == null)
        instance = Instance();

3
由于缺少分号,目前它无法编译。 - Filburt
或者,你可以使用Lazy<T>,它能更清晰地显示你的意图且线程安全。 - Sriram Sakthivel
7个回答

12

lock锁定整个代码块。由于它后面没有花括号({}),因此它锁定一个隐式的代码块 - if语句。在这里,相同的逻辑适用 - 如果条件满足,if执行代码块。由于它也没有跟随花括号,它隐含地包含一个只有单个语句的代码块。换句话说,给定的代码等效于:

lock (LockObject) {
    if (instance == null) {
        instance = Instance();
    }
}

5

if语句不能单独执行,需要在其后添加代码块来执行表达式中的true情况。所以正如@Mureinik已经指出的那样,lock会锁定整个初始化块。你甚至可以这样写:

lock (LockObject) if (instance == null) instance = Instance();

然而,在这种情况下不建议使用没有花括号的代码编写,因为这样非常容易混淆和难以调试。同时请注意,lock 语句是一种语法糖,用于使用 Monitor 类,您的代码会被编译成以下形式:

try
{
    Monitor.Enter(LockObject);
    if (instance == null)
    {
        instance = Instance();
    }
}
finally
{
    Monitor.Exit(LockObject);
}

我想指出的是,对于初始化逻辑,您可以使用Lazy<T>类,它是线程安全的,并且使用的结构不像Monitor那么重,速度也比您的代码快。代码将如下所示:

// field in class
Lazy<Instance> lazyInstance = new Lazy<Instance>(() => Instance());

//usage in code
var instanceValue = lazyInstance.Value;

3

Lock被C#编译器翻译为Monitor.EnterMonitor.Exit。这段C#代码

static void Main(string[] args)
{
    lock (LockObject)
        if (instance == null)
            instance = Instance();
    Console.WriteLine(instance == null);
}

以下是 IL 代码,清晰地显示了在 instance 被赋值(L_0026)之后调用了 Monitor.Exit(L_0036)。

这两行代码都被锁定。

.method private hidebysig static void Main(string[] args) cil managed
{
  .entrypoint
  .maxstack 2
  .locals init (
    [0] bool flag,
    [1] object obj2,
    [2] bool flag2)
  L_0000: nop
  L_0001: ldc.i4.0
  L_0002: stloc.0
  L_0003: ldsfld object TestLock.Program::LockObject
  L_0008: dup
  L_0009: stloc.1
  L_000a: ldloca.s flag
  L_000c: call void [mscorlib]System.Threading.Monitor::Enter(object, bool&)
  L_0011: nop
  L_0012: ldsfld object TestLock.Program::instance
  L_0017: ldnull
  L_0018: ceq
  L_001a: ldc.i4.0
  L_001b: ceq
  L_001d: stloc.2
  L_001e: ldloc.2
  L_001f: brtrue.s L_002b
  L_0021: call object TestLock.Program::Instance()
  L_0026: stsfld object TestLock.Program::instance
  L_002b: leave.s L_003d
  L_002d: ldloc.0
  L_002e: ldc.i4.0
  L_002f: ceq
  L_0031: stloc.2
  L_0032: ldloc.2
  L_0033: brtrue.s L_003c
  L_0035: ldloc.1
  L_0036: call void [mscorlib]System.Threading.Monitor::Exit(object)
  L_003b: nop
  L_003c: endfinally
  L_003d: nop
  L_003e: ldsfld object TestLock.Program::instance
  L_0043: ldnull
  L_0044: ceq
  L_0046: call void [mscorlib]System.Console::WriteLine(bool)
  L_004b: nop
  L_004c: ret
  .try L_0003 to L_002d finally handler L_002d to L_003d
}

1
它锁定了所有的代码。这里的 {} 用于 if 的部分被省略了。

1
"If"语句包括一个“then”情况的语句。因此锁适用于这两行。
一个简单的经验法则是:如果有一个{,它适用于匹配的}之前的所有内容,否则它适用于第一个;之前的所有内容。这并不涵盖所有情况,但肯定涵盖了最常见的情况。

0

它锁定了整个语句。考虑以下示例:

 lock (LockObject)
 {
     if (instance == null) {
 }

如果只是在使用大括号时锁定if条件,那么由于排列/匹配不当,它将导致编译器错误。

0

这段代码片段锁定了两行代码。

在LockObject上的锁定将不会被释放,直到if语句执行完成。这意味着,如果第2行中的条件为真,则会在释放第1行中的锁之前执行第3行。

希望能对您有所帮助 :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接