究竟什么“锁定”锁定?

时间:2014-02-04 05:44:44

标签: c# multithreading locking

我正在创建一个创建和管理多个套接字连接的类,它将把从套接字接收的信息转发到主线程,反之亦然。多线程,我传递了大量信息,这对我来说是新的(就像C#中的大多数事情一样),所以我需要澄清locks究竟做了什么。

当我锁定某个方法的一部分时,它只确保没有其他线程可以进入该部分代码,或者它是否确实阻止该部分代码中的所有变量被其他线程修改而不管它们发生在何处?

例如:

public class TestLock
{
    public volatile int a = 0;
    public volatile int b = 2;
    private object ALock = new Object();

    public TestLock() { }

    public void UnlockEdit(int atemp, int btemp)
    {
        a = atemp;
        b = btemp;
    }

    public void LockedEdit(int atemp, int btemp)
    {


        lock(ALock)
        {
            a = atemp;
            b = btemp;
        }
    }

    public int[] ReturnValues()
    {
        int[] ret = new int[2];

        lock (ALock)
        {
            ret[0] = a;
            ret[1] = b;
        }

        return ret;
    }

}

如果线程 A 调用LockedEdit方法并在线程 B 进入UnlockEdit之前稍微到达锁定。怎么了?锁定会阻止线程 B 修改ab吗?它会阻止吗?或者锁只适用于一个特定的代码分支?或者我应该将相同的lock对象(ALock)应用于我想要修改和从多个线程读取的对象的每个读写方法吗?

3 个答案:

答案 0 :(得分:7)

  

当我锁定某个方法的一部分时,它只确保没有其他线程可以进入该部分代码,或者它是否确实阻止该部分代码中的所有变量被其他线程修改而不管它们发生在何处?

这个问题是如此复杂和不完整,以至于无法回答。让我们改写它。

  

当我锁定方法的某个部分时,是否确保没有其他线程可以进入该部分代码?

是。然而,更准确的说明方式是:在获得与赋予lock语句的“monitor”对象相关联的锁之前,不能输入锁定的部分。该锁定一次只能由一个线程获得。

我还注意到,在涉及监视器的更高级用法的场景中如何获得锁定存在微妙之处,例如脉冲。再次,请仔细阅读文档,以获取有关监视器操作的确切语义的说明。

  

当我锁定某个方法的一部分时,确保没有其他线程可以进入该部分代码吗?

没有。采取锁定还有其他行动,而不仅仅是确保阻止相互排斥。例如,它引入了一个内存屏障。

  

它是否实际上阻止了该部分代码中的所有变量被其他线程修改,无论它们出现在何处?

当然不是!

  

锁是否会阻止线程B修改a和b?

你建造了一个带门的浴室,门的一侧有一个锁,另一侧有一个通向外面的门。你锁上卫生间的门。这似乎是一种有效的方法,确保每次只能挤出一个牙膏吗?

  

它会阻止吗?

会阻止什么?

  

锁只适用于一个特定的代码分支吗?

我无法理解这个问题。

  

我应该将相同的“锁定”对象(ALock)应用于我想要修改和从多个线程读取的对象的每个读写方法吗?

你没有提出但应该提出的问题:

  

我首先应该使用共享内存多线程吗?

可能不是。即使是专家也很难做到这一点。如果我正在进行套接字编程,我首先使用async-await或任务并行库来管理我的异步,而不是直接参与线程。线程是专为专家设计的非常低级的工具;尝试将抽象级别提升几个级别。

答案 1 :(得分:1)

lock语句会在您的代码中创建critical section。这可以防止其他线程进入锁定语句括号内的代码。其他线程基本上等到轮到他们进入该部分。

它不会阻止其他线程修改类中的变量。例如,TestLock.a和TestLock.b可以通过TestLock类中不使用Lock(ALock)语句的函数进行修改。另外,由于变量a和b是公共的,因此可以通过类外部的代码修改(在单独的线程上)。

以下是.NET Basic Threading的良好链接:

来自MSDN:

lock关键字确保一个线程不进入代码的关键部分,而另一个线程处于临界区。如果另一个线程试图输入一个锁定的代码,它将等待,阻止,直到该对象被释放。

以下是一些保护变量对多线程应用程序安全的代码示例(取自C# 3.0 cookbook):

    public static class SaferMemberAccess
        {

            private static int numericField = 1;
                 private static object syncObj = new object( );

            public static void IncrementNumericField( )
           {
                     lock(syncObj)              
                         {
                       ++numericField;
                     }
           }

            public static void ModifyNumericField(int newValue)
            {
                     lock(syncObj)             
                         {
                       numericField = newValue;
                     }
            }

            public static int ReadNumericField( )
            {
                     lock (syncObj)            
                         {
                       return (numericField);
                     }
            }
        }

答案 2 :(得分:1)

你在这里获得对象的锁定

lock(ALock)

此代码的这一部分现在被此对象锁定,并且在释放锁之前没有其他对象可以进入,即此对象离开临界区。

这也意味着此时没有其他对象能够在关键部分修改此对象锁定的变量。