繁体   English   中英

锁定传递的对象会发生什么?

[英]Lock what happens to passed object?

在一个项目中,我正在玩线程。 我试图建立一个不会“破坏”数据的安全线程。 我的线程在后台运行,并在另一类上调用函数,我可以调用Go和Go2,一个函数在列表中添加和删除。 我不希望它们同时运行,以下情况有什么区别:

static readonly object _locker1 = new object();
static readonly object _locker2 = new object();


public void Go(Object something)
{
  lock (_locker1)
  {
    myList.add(something);
  }
}

public void Go2(Object something)
{
  lock (_locker2)
  {
    myList.Remove(something);
  }
}

如果我将Go2替换为:

public void Go2(Object something)
{
  lock (_locker1)
  {
    myList.Remove(something);
  }
}

请注意lock参数。

第三种情况可以帮助我理解,可以说我从另一个线程(线程2)调用Go,它可以运行,因为_locker1被线程2锁定,而Go2(其_locker 1被线程2锁定)从线程1调用了吗?

static readonly object _locker1 = new object();
static readonly object _locker2 = new object();


public void Go(Object something)
{
  lock (_locker1)
  {
    //Can I call Go2 which is locked by the same object?
    Go2(something);
  }
}

public void Go2(Object something)
{
  lock (_locker1)
  {
    myList.Remove(something);
  }
}

有人可以解释传递给锁的值的作用吗?

非常简单:如果两个锁使用相同的对象,则它们将不会同时运行。 在您的第一个代码段中,由于Go和Go2锁定在不同的对象上,它们可以同时运行并做坏事。

文档所述

通过获取给定对象的互斥锁 ,执行语句,然后释放该锁,lock关键字将语句块标记为关键部分。

因此, lock是给定对象的监视器的语法糖。 当您使用第一个代码示例时,如果两个线程都执行Go (或Go2 ),则它们将不得不彼此等待,而如果两个线程执行不同的方法,则可能导致同步冲突。

在第二个代码示例中,由于始终锁定同一对象的解锁,因此可以保证不能同时执行myList.addmyList.remove

编辑 :在第三种情况下, 将计算递归锁 这意味着,如果第一个线程调用Go而第二个线程调用Go2 ,则如果第一个线程首先进入lock ,它将对Go2进行调用以获取对该锁的访问权,删除该项,从递归调用中返回,离开lock ,只有第二个线程才能进入Go2的锁定。 万一第二个线程在比赛中获胜,第二个线程将首先进入lock并从外lock阻塞第一个线程。 仅当第二个线程离开Go2lock时,第一个线程才能进入Golock (并执行递归调用)。

锁定语句实际上转换为引擎盖下的监视器锁定,例如:

bool lockWasTaken = false;
var temp = obj;
try
{
    Monitor.Enter(temp, ref lockWasTaken);
    // body
}
finally
{
    if (lockWasTaken)
    {
        Monitor.Exit(temp); 
    }
}

若要查找有关Monitor锁定如何工作的更多信息,请参见: MSDN文档

引用MSDN:

使用Enter键获取作为参数传递的对象的Monitor。 如果另一个线程在对象上执行了Enter键,但尚未执行相应的Exit,则当前线程将阻塞,直到另一个线程释放对象为止。 同一线程多次调用Enter而不阻塞它是合法的; 但是,必须先调用相等数量的Exit调用,然后其他等待该对象的线程才能解除阻塞。 使用监视器锁定对象(即引用类型),而不是值类型。 当您将值类型变量传递给Enter时,它将被装箱为对象。 如果再次将相同的变量传递给Enter,则会将其装箱为单独的对象,并且该线程不会阻塞。 在这种情况下,Monitor应该保护的代码不受保护。 此外,将变量传递给Exit时,还会创建另一个单独的对象。 因为传递给Exit的对象与传递给Enter的对象不同,所以Monitor抛出SynchronizationLockException。 有关更多信息,请参见概念性主题监视器。

这意味着您的代码将在2个不同的对象上被阻止,而您只想在一个对象上进行阻止以使其线程安全。

传递给lock的值是共享状态的象征,该共享状态将在lock语句的块(大括号)中访问。 对该值进行锁定的每个请求将按顺序排队和处理。 一次只允许处理该值的一个请求者,因此一次只能由一个请求者访问共享状态。

抽象化

就像我和橡皮鸡见面一样,我说“只有拿着橡皮鸡的人才能讲话”。 在这种情况下,橡皮鸡是锁定参数,说话能力是共享资源。 每个想发言的人都将排成一列。 只有一个人可以握住鸡,所以只有一个人可以说话。 说话的人完成后,他们将鸡肉递给下一个排队的人。

在第一种情况下,您有两只橡皮鸡:更衣室1(橡胶鸡1)和更衣室2(橡胶鸡2)。 因此,Go和Go2不会彼此等待转身(它们都有一只鸡!)。 一个调用Go的线程将能够添加到myList中,而另一个调用Go2的线程可以同时访问myList以便从列表中删除该项目。 但是,调用Go的两个线程将轮流等待,因为它们都需要相同的橡皮鸡:locker1; 两个调用Go2的线程也是如此。

如果您使Go和Go2使用相同的值(同一只鸡),那么他们将不得不等待回合以获取对该值的锁定。 这将阻止它们一个调用Go的线程和另一个调用Go2的线程同时访问myList。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM