lock 语句获取给定对象的互斥锁,执行语句块,然后释放锁。当锁被持有时,持有该锁的线程可以再次获取并释放该锁。任何其他线程都被阻止获取锁并等待,直到锁被释放。lock 语句确保在任何时刻最多只有一个线程执行它的线程体。

lock 语句采用以下形式:

lock ( x )
{
    // 你的代码……
}

变量 x 是 System . Threading . Lock 类型或引用类型的表达式。当 x 在编译时已知为 System . Threading . Lock 类型时,它精确地等价于:

using ( x . IW ( ) )
{
    // 你的代码……
}

由 Lock . IW ( ) 返回的对象是一个包含 Dispose ( ) 方法的重构。生成的 using 语句确保释放范围,即使在 lock 语句的主体中抛出异常。

否则,lock 语句就完全等价于:

object _dx锁定 = x;
bool _解锁 = false;
try
{
    System . Threading . Monitor . Enter ( _dx锁定 , ref _解锁 );
    // 你的代码……
}
finally
{
    if ( _解锁 ) System . Threading . Monitor . Exit ( _dx锁定 );
}

由于代码使用了 try……finally…… 语句,因此即使在锁语句体中抛出异常,也会释放锁。

不能在锁语句体中使用 await 表达式。由于代码使用了 try……finally…… 语句,因此即使在锁语句体中抛出异常,也会释放锁。

不能在锁语句体中使用 await 表达式。

导引

从 .NET 9 和 C# 13 开始,锁定 System . Threading . Lock 类型的专用对象实例以获得最佳性能。此外,如果已知的 Lock 对象被强制转换为另一种类型并被锁定,编译器会发出警告。如果使用旧版本的 .NET 和 C#,锁定不用于其他目的的专用对象实例。避免对不同的共享资源使用相同的锁对象实例,因为这可能导致死锁或锁争用。特别要避免使用以下实例作为锁对象:

  • 因为调用者也可能锁定这个
  • type 实例,因为它们可以通过 typeof 操作符或反射获得
  • string 实例,包括 string 字面量,因为它们可能被拘禁
  • 保持锁的时间尽可能短,以减少锁争用

例子

下面的示例定义了一个 Account 类,该类通过锁定专用的 balanceLock 实例来同步对其私有 balance 字段的访问。使用相同的锁定实例确保两个不同的线程不能通过同时调用 Debit 或 Credit 方法来更新 balance 字段。示例使用 C# 13 和新的 Lock 对象。如果您使用的是旧版本的 C# 或旧版本的 .NET 库,请锁定 object 的实例。

public class 账户
    {
        // 在 C# 13 之前使用的“对象”
        private readonly System . Threading . Lock _锁定利率 = new ( );
        private decimal _利率;

        public 账户 ( decimal 初始化利率 ) => _利率 = 初始化利率;

        public decimal 借款 ( decimal 金额 )
            {
                if ( 金额 < 0 )
                    {
                        throw new ArgumentOutOfRangeException ( nameof ( 金额 ) , "借款金额不能是负数。" );
                    }

                    decimal 实际借款 = 0;
                    lock ( _锁定利率 )
                        {
                            if ( _利率 >= 金额 )
                                {
                                    _利率 -= 金额;
                                    实际借款 = 金额;
                                }
                        }
                    return 实际借款;
                    }

    public void 贷款 ( decimal 贷款 )
        {
            if ( 贷款 < 0 )
                {
                    throw new ArgumentOutOfRangeException ( nameof ( 贷款 ) , "贷款金额不能是负数。" );
                }

            lock ( _锁定利率 )
                {
                    _利率 += 贷款;
                }
            }

    public decimal 获取存款 ( )
        {
            lock ( _锁定利率 )
                {
                    return _利率;
                }
        }
    }

class Lei账户测试
    {
        static async Task Main ( )
            {
                var 新账户 = new 账户( 1000 );
                var rws = new Task [ 100 ];
                for ( int i = 0 ; i < rws . Length ; i++ )
                    {
                        rws [ i ] = Task . Run ( ( ) => Update ( 新账户 ) );
                    }
                await Task . WhenAll ( rws );
                Console . WriteLine ( $"账户的存款:{新账户 . 获取存款 ( )}" );
                 // 输出:
                // 账户的存款:2000
    }

static void Update ( 账户 account )
    {
        decimal [ ] 本金s = [ 0 , 2 , -3 , 6 , -2 , -1 , 8 , -5 , 11 , -6 ];
        foreach ( var 本金 in 本金s )
            {
                if ( 本金 >= 0 )
                    {
                        account . 贷款 ( 本金 );
                    }
                else
                    {
                        account . 借款 ( Math . Abs ( 本金 ) );
                    }
            }
     }

兔子码农
4 声望1 粉丝

一个酒晕子


下一篇 »
C# 的 namespace