有人可以向我解释条件同步吗?
一个例子(最好在 C# 中)也将不胜感激。
听起来你的教授在谈论线程。线程使计算机程序一次可以做不止一件事。在一个已经运行的线程中启动一个新线程的行为被计算机程序员称为“启动一个线程”。
线程可以共享相同的内存空间。条件同步(或仅仅是同步)是保护内存区域不被两个不同线程同时修改的任何机制。
假设您外出购物,而妻子在家支付账单。这是一个幼稚的例子,它在现实生活中并没有真正以这种方式工作,但它可以作为一个简单的例子。
你的妻子正在网上支付账单。同时,您正在杂货店刷信用卡。这两种行为都涉及将资金从您的支票账户中转出。为了模拟这个活动,我们编写了以下代码:
public class MyBanking
{
static double myAccountBalance;
//
public void DebitAccount(double debitAmount)
{
Console.Writeline("Your Old Balance is: " + myAccountBalance.ToString());
Console.Writeline("Your Debit is: " + debitAmount.ToString());
myAccountBalance = myAccountBalance - amount;
Console.Writeline("Your New Balance is: " + myAccountBalance.ToString());
}
}
假设您的妻子正在一个线程上运行此类的一个实例(“副本”),而您正在另一个线程上运行一个实例。myAccountBalance 变量被声明为静态变量,以允许它在两个正在运行的实例之间共享(您和您的妻子只有一个支票账户)。
您可以通过调用如下代码进行借记:
MyBanking bankingObject = new MyBanking();
bankingObject.DebitAccount(100);
你的妻子同时扣款:
MyBanking bankingObject = new MyBanking();
bankingObject.DebitAccount(50);
如果您的线程在您的旧余额打印到屏幕上之后,但在打印新余额之前被您妻子的线程中断,会发生什么?您妻子的线程从帐户中扣除,并将控制权返回给您的线程。你的妻子在屏幕上看到这个:
Your Old Balance is: 2000
Your Debit is: 50
Your New Balance Is: 1950
当电脑在你的屏幕上打印新的余额时,它是错误的,因为你妻子的借记也会被计算在内。你会看到这样的东西:
Your Old Balance is: 2000
Your Debit is: 100
Your New Balance Is: 1850
为了解决这个问题,我们用 lock 语句包围我们的方法代码。lock 语句导致所有其他线程等待我们的实例完成。新代码如下所示:
public class MyBanking
{
static double myAccountBalance;
//
public void DebitAccount(double debitAmount)
{
lock (this)
{
Console.Writeline("Your Old Balance is: " + myAccountBalance.ToString());
Console.Writeline("Your Debit is: " + debitAmount.ToString());
myAccountBalance = myAccountBalance - amount;
Console.Writeline("Your New Balance is: " + myAccountBalance.ToString());
}
}
}
你妻子的线程现在将在你妻子的代码开始执行之前等待你在 lock 语句中的代码完成执行。您的新余额现在将是正确的,因为在您完成交易时,您妻子的线程不再可能更改余额。在您的屏幕上,您现在将看到:
Your Old Balance is: 2000
Your Debit is: 100
Your New Balance Is: 1900
你的妻子会看到这个:
Your Old Balance is: 1900
Your Debit is: 50
Your New Balance Is: 1850
这是同步。
基本上它是需要的线程的设计模式
a) 同步访问资源
b) 有时会等待其他线程,直到满足某个条件
您在 C# 上下文中提出此问题,.NET 使用 Monitor.Wait 和 Monitor.Pulse(以及围绕各种 Win32 对象的包装器,如 WaitEventhandle)提供对此的支持。
这是关于 SO的一个很好的队列示例。
主要技术细节如下:
lock(buffer) // is Monitor.Enter(buffer) ... finally Monitor.Leave(buffer)
{
while (buffer.Count < 1)
{
Monitor.Wait(buffer);
}
...
}
请注意那里是如何锁定等待的。它看起来像死锁,但 Wait 会在等待时释放锁。内部的代码在lock() { }
运行时仍然具有对缓冲区的独占访问权限。
然后另一个线程必须在将某些内容放入缓冲区时发出信号:
Monitor.Pulse(buffer);
上面的代码几乎是正确的,但实际上是错误的。通过使用lock(this)
,您将只锁定您的MyBanking
类实例,但您的妻子将锁定她的。要锁定对共享变量的访问,您可以锁定类型(即lock(typeof(MyBanking))
),或者您可以引入一个新的共享变量并锁定它(您不能锁定一个 int 所以通常人们按如下方式创建一个对象。
class MyBanking
{
static object lockObj = new object();
static double myAccountBalance;
public void DebitAccount(double debitAmount)
{
lock (lockObj)
同步已经清楚地解释过了。但是,条件同步明确规定进程/线程仅在满足某些条件后才执行。通常,条件是某个其他进程/线程已经执行。
在借记帐户和查看余额的给定示例中。如果查看您的余额是一种单独的同步方法,并且我们只想在您的帐户被扣款后查看余额,那么这将需要条件同步。
生产者-消费者问题很好地描述了条件同步。