在一个正在玩线程的项目中。我试图建立一个不会“破坏”数据的安全线程。我的线程在后台运行,并在另一个类上调用函数,我可以调用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);
}
}
请注意锁定参数。
第三种情况可以帮助我理解,假设我从一个不同的线程(thread2)调用Go,它可以运行,因为_locker1被thread2锁定而Go2(它具有被thread2锁定的_locker 1)是从线程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);
}
}
有人可以解释传递给lock的值是什么吗?
答案 0 :(得分:5)
这非常简单:如果两个锁使用同一个对象,它们将不会同时运行。在你的第一个片段中,当Go和Go2锁定不同的对象时,它们可以同时运行并做坏事。
答案 1 :(得分:2)
正如documentation所说:
因此,lock关键字通过获取给定对象的互斥锁,执行语句,然后释放锁定,将语句块标记为关键部分。
lock
是给定对象的监视器的语法糖。当您使用第一个代码示例时,如果两个线程都执行Go
(或Go2
),则它们必须等待彼此,而如果两个执行不同的方法,则可能导致同步冲突。 / p>
在第二个代码示例中,由于您始终锁定解锁同一个对象,因此可以确保不能同时执行myList.add
和myList.remove
。
编辑:如遇第三种情况,recursive locks are counted。这意味着如果第一个线程调用Go
而第二个线程调用Go2
,如果第一个线程首先进入lock
,它将调用Go2
获取访问锁,删除项,从递归调用返回,离开lock
,然后第二个线程才能进入Go2
的锁。如果第二个线程赢得比赛,第二个线程将首先进入lock
并阻止来自外部lock
的第一个线程。只有当第二个线程离开lock
Go2
时,第一个线程才能进入lock
的{{1}}(并执行递归调用)。
答案 2 :(得分:1)
锁定语句实际上转换为引擎盖下的监视器锁,例如:
bool lockWasTaken = false;
var temp = obj;
try
{
Monitor.Enter(temp, ref lockWasTaken);
// body
}
finally
{
if (lockWasTaken)
{
Monitor.Exit(temp);
}
}
要了解有关Monitor锁定工作方式的更多信息,请查看此处:MSDN Documentation
引用MSDN:
使用Enter获取作为参数传递的对象的Monitoron。如果另一个线程在对象上执行了Enter但尚未执行相应的Exit,则当前线程将阻塞,直到另一个线程释放该对象。如果没有阻塞,同一个线程不止一次调用Enter是合法的;但是,在等待对象的其他线程将解除阻塞之前,必须调用相同数量的Exit调用。 使用Monitor锁定对象(即引用类型),而不是值类型。将值类型变量传递给Enter时,它将被装箱为对象。如果再次将同一个变量传递给Enter,则将其作为单独的对象加框,并且该线程不会阻塞。在这种情况下,Monitor应该保护的代码不受保护。此外,当您将变量传递给Exit时,仍会创建另一个单独的对象。因为传递给Exit的对象与传递给Enter的对象不同,所以Monitor会抛出SynchronizationLockException。有关更多信息,请参阅概念主题监视器。
这意味着您的代码将被阻止在2个不同的对象上,您只想阻止它们使其线程安全。
答案 3 :(得分:1)
传递给lock的值是将在块(花括号)中访问lock语句的共享状态的象征。锁定该值的每个请求都将按顺序排队和处理。一次只允许一个请求者处理该值,因此共享状态一次只能被一个请求者访问。
<强>抽象强>
如果我和一只橡皮鸡会面,我就说#34;只有 持橡皮鸡的人可以说&#34;。在这 场景中,橡皮鸡是锁参数,能力 说话是共享资源。每个想发言的人都会形成一个 线。只有一个人可以抓鸡,所以只有一个人可以 说话。当说话的人完成后,他们将鸡肉交给了 排在第二位的人。
在您的第一种情况下,您有两只橡胶鸡:储物柜1(橡皮鸡1)和储物柜2(橡皮鸡2)。因此,Go和Go2不会在一个转弯处等待(他们都有一只鸡!)。调用Go的线程将能够添加到myList,而另一个调用Go2的线程可以同时访问myList以从列表中删除该项目。然而,两个线程调用Go将等待轮到他们,因为他们都需要相同的橡皮鸡:locker1;调用Go2的两个线程也是如此。
如果你让Go和Go2都使用相同的值(相同的鸡),那么他们将不得不等待他们获得该值的锁定。这将阻止他们一个调用Go的线程和另一个调用Go2的线程同时访问myList。