C# lock

Why is lock(this) {…} bad?

https://stackoverflow.com/questions/251391/why-is-lockthis-bad

It is bad form to use this in lock statements because it is generally out of your control who else might be locking on that object.

In order to properly plan parallel operations, special care should be taken to consider possible deadlock situations, and having an unknown number of lock entry points hinders this. For example, any one with a reference to the object can lock on it without the object designer/creator knowing about it. This increases the complexity of multi-threaded solutions and might affect their correctness.

A private field is usually a better option as the compiler will enforce access restrictions to it, and it will encapsulate the locking mechanism. Using this violates encapsulation by exposing part of your locking implementation to the public. It is also not clear that you will be acquiring a lock on this unless it has been documented. Even then, relying on documentation to prevent a problem is sub-optimal.

Finally, there is the common misconception that lock(this) actually modifies the object passed as a parameter, and in some way makes it read-only or inaccessible. This is false. The object passed as a parameter to lock merely serves as a key. If a lock is already being held on that key, the lock cannot be made; otherwise, the lock is allowed.

This is why it's bad to use strings as the keys in lock statements, since they are immutable and are shared/accessible across parts of the application. You should use a private variable instead, an Object instance will do nicely.

Run the following C# code as an example.

using Xunit.Abstractions;
using System;
using System.Threading;
using Xunit;
using Xunit.Abstractions;

namespace AssemblyTest
{
    public class TestBase 
    {
        protected readonly ITestOutputHelper Output;

        public TestBase(ITestOutputHelper tempOutput)
        {
            Output = tempOutput;
        }
    }
}

public class Person
    {
        public int Age { get; set; }
        public string Name { get; set; }

        public void LockThis()
        {
            lock (this)
            {
                System.Threading.Thread.Sleep(10000);
            }
        }
    }

    public class LockTest : TestBase
    {
        public LockTest(ITestOutputHelper tempOutput) : base(tempOutput)
        {
        }

        [Fact]
        public void Test()
        {
            var nancy = new Person {Name = "Nancy Drew", Age = 15};
            var a = new Thread(nancy.LockThis);
            a.Start();
            var b = new Thread(Timewarp);
            b.Start(nancy);
            Thread.Sleep(10);
            var anotherNancy = new Person {Name = "Nancy Drew", Age = 50};
            var c = new Thread(NameChange);
            c.Start(anotherNancy);
            a.Join();
        }

        private void Timewarp(object subject)
        {
            var person = subject as Person;
            if (person == null) throw new ArgumentNullException("subject");
            // A lock does not make the object read-only.
            lock (person.Name)
            {
                while (person.Age <= 23)
                {
                    // There will be a lock on 'person' due to the LockThis method running in another thread
                    if (Monitor.TryEnter(person, 10) == false)
                    {
                        Output.WriteLine("'this' person is locked!");
                    }
                    else Monitor.Exit(person);

                    person.Age++;
                    if (person.Age == 18)
                    {
                        // Changing the 'person.Name' value doesn't change the lock...
                        person.Name = "Nancy Smith";
                    }

                    Output.WriteLine("{0} is {1} years old.", person.Name, person.Age);
                }
            }
        }

        private void NameChange(object subject)
        {
            var person = subject as Person;
            if (person == null) throw new ArgumentNullException("subject");
            // You should avoid locking on strings, since they are immutable.
            if (Monitor.TryEnter(person.Name, 30) == false)
            {
                Output.WriteLine(
                    "Failed to obtain lock on 50 year old Nancy, because Timewarp(object) locked on string \"Nancy Drew\".");
            }
            else Monitor.Exit(person.Name);

            if (Monitor.TryEnter("Nancy Drew", 30) == false)
            {
                Output.WriteLine(
                    "Failed to obtain lock using 'Nancy Drew' literal, locked by 'person.Name' since both are the same object thanks to inlining!");
            }
            else Monitor.Exit("Nancy Drew");

            if (Monitor.TryEnter(person.Name, 10000))
            {
                string oldName = person.Name;
                person.Name = "Nancy Callahan";
                Output.WriteLine("Name changed from '{0}' to '{1}'.", oldName, person.Name);
            }
            else Monitor.Exit(person.Name);
        }
    }

 

Why does the lock object have to be static?

It isn't "very common to use a private static readonly object for locking in multi threading" - rather, it is common to use a lock at the appropriate / chosen granularity. Sometimes that is static. More often, IMO, it isn't - but is instance based.

The main time you see a static lock is for a global cache, or for deferred loading of global data / singletons. And in the latter, there are better ways of doing it anyway.

So it really depends: how is Locker used in your scenario? Is it protecting something that is itself static? If so, the lock should be static. If it is protecting something that is instance based, then IMO the lock should also be instance based.

 

lock statement (C# Reference)

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/lock-statement

 

Locking

http://www.albahari.com/threading/part2.aspx#_Locking

C#’s lock statement is in fact a syntactic shortcut for a call to the methods Monitor.Enter and Monitor.Exit, with a try/finally block. Here’s (a simplified version of) what’s actually happening within the Go method of the preceding example:

Monitor.Enter (_locker);
try
{
  if (_val2 != 0) Console.WriteLine (_val1 / _val2);
  _val2 = 0;
}
finally { Monitor.Exit (_locker); }

Calling Monitor.Exit without first calling Monitor.Enter on the same object throws an exception.

 

03-13
<think>好的,我现在需要回答用户关于C#lock关键字的用法、使用方法和示例的问题。首先,我要回顾一下用户提供的引用内容,确保回答准确。 用户提到了四个引用,其中引用[1]到[4]都涉及lock关键字的使用注意事项和相关概念。我需要把这些信息整合到回答中,同时遵循用户的系统级指令,比如正确使用LaTeX格式和生成相关问题。 首先,我应该解释lock关键字的基本作用,即同步线程访问共享资源,防止竞争条件。根据引用[2],lock用于确保同一时间只有一个线程执行代码块。引用[3]提到它通过获取互斥锁来实现这一点。 接下来,使用方法部分需要强调正确的锁对象选择。根据引用[1],应该使用私有只读对象实例作为锁对象,避免使用this、Type实例或字符串。这部分需要详细说明,并给出代码示例。 示例部分,我需要提供一个简单的例子,比如账户余额的操作,展示如何用lock保护共享资源。同时,可以对比错误用法,如使用public对象或字符串,说明其潜在问题,如死锁或锁争用,如引用[1]所述。 注意事项中,要提到lock的内部机制(Monitor.Enter和Monitor.Exit),避免嵌套锁,以及锁的范围。引用[4]指出volatile的不足,强调在复合操作中需使用lock或其他同步机制,这点也应包括在内。 最后,生成相关问题时要围绕lock关键字的最佳实践、替代方案、与Monitor类的关系、常见错误和应用场景展开,确保问题有针对性。 需要确保回答结构清晰,分步骤说明,并正确引用来源。同时检查LaTeX语法是否正确,比如变量用$...$,但可能用户的问题中数学内容不多,所以可能不需要太多LaTeX。但如果有代码块或术语,要正确格式化。 现在整理这些思路,组织成用户要求的格式,先回答用法、方法、示例,再注意事项,最后相关问题。确保每个引用在适当的位置标注,如引用[1]关于锁对象的选择,引用[4]关于复合操作的处理。</think>### C# lock 关键字使用方法详解 #### 1. 基本作用 lock 关键字用于创建线程临界区,确保同一时间**只有一个线程**可以访问共享资源。通过获取指定对象的互斥锁(mutex)来实现线程同步,解决资源竞争和数据不一致问题[^2][^3]。 #### 2. 标准语法格式 ```csharp private readonly object _lockObj = new object(); void ThreadSafeMethod() { lock (_lockObj) // 获取锁 { // 临界区代码 // 对共享资源的操作 } // 自动释放锁 } ``` #### 3. 正确使用规范 - **锁对象选择原则** 1. 必须使用专用对象:`private readonly object` 实例[^1] 2. 禁止使用危险对象: - `this`(调用方可能已锁定) - `Type` 实例(通过`typeof`或反射获取) - 字符串(可能被暂存池共享) ```csharp // 正确示例 private readonly object _safeLock = new object(); // 错误示例 lock ("myLock") { ... } // 字符串暂存风险 lock (this) { ... } // 外部访问风险 ``` #### 4. 典型应用示例 **银行账户线程安全操作** ```csharp class BankAccount { private decimal _balance; private readonly object _balanceLock = new object(); public void Deposit(decimal amount) { lock (_balanceLock) { _balance += amount; // 原子操作保障 } } public bool Withdraw(decimal amount) { lock (_balanceLock) { if (_balance >= amount) { _balance -= amount; return true; } return false; } } } ``` #### 5. 高级注意事项 1. **锁机制本质** 编译后等价于`Monitor.Enter`和`Monitor.Exit`的组合: ```csharp Monitor.Enter(_lockObj); try { // 临界区代码 } finally { Monitor.Exit(_lockObj); } ``` 2. **锁嵌套风险** 同一线程可重复获取已持有的锁,但跨线程嵌套锁可能导致死锁: ```csharp // 危险嵌套模式 lock (objA) { lock (objB) { // 若另一线程以相反顺序锁定,将导致死锁 // ... } } ``` 3. **锁粒度控制** - 粗粒度锁:保护多个相关资源,可能降低并发性能 - 细粒度锁:为每个资源单独加锁,增加实现复杂度 #### 6. 错误用法对比 ```csharp // 反例1:使用公共对象 public object publicLock = new object(); lock (publicLock) { ... } // 外部代码可能破坏锁定规则 // 反例2:修改锁对象 private object mutableLock = new object(); lock (mutableLock) { mutableLock = new object(); // 导致后续线程锁定不同对象 } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值