C#lock构造的误解(以msdn代码为例)
我对使用C#C#lock构造的误解(以msdn代码为例),c#,multithreading,C#,Multithreading,我对使用C#锁构造感兴趣 现在是MSDN中的样本,然后是以下主要问题: 以下示例使用线程和锁。只要lock语句存在,语句块就是一个关键部分,余额永远不会变成负数 class Account { private Object thisLock = new Object(); int balance; Random r = new Random(); public Account(int initial) { balance = initi
锁
构造感兴趣
现在是MSDN中的样本,然后是以下主要问题:
以下示例使用线程和锁。只要lock语句存在,语句块就是一个关键部分,余额永远不会变成负数
class Account
{
private Object thisLock = new Object();
int balance;
Random r = new Random();
public Account(int initial)
{
balance = initial;
}
int Withdraw(int amount)
{
// This condition never is true unless the lock statement
// is commented out.
if (balance < 0)
{
throw new Exception("Negative Balance");
}
// Comment out the next line to see the effect of leaving out
// the lock keyword.
lock (thisLock)
{
if (balance >= amount)
{
Console.WriteLine("Balance before Withdrawal : " + balance);
Console.WriteLine("Amount to Withdraw : -" + amount);
balance = balance - amount;
Console.WriteLine("Balance after Withdrawal : " + balance);
return amount;
}
else
{
return 0; // transaction rejected
}
}
}
public void DoTransactions()
{
for (int i = 0; i < 100; i++)
{
Withdraw(r.Next(1, 100));
}
}
}
class Test
{
static void Main()
{
Thread[] threads = new Thread[10];
Account acc = new Account(1000);
for (int i = 0; i < 10; i++)
{
Thread t = new Thread(new ThreadStart(acc.DoTransactions));
threads[i] = t;
}
for (int i = 0; i < 10; i++)
{
threads[i].Start();
}
//block main thread until all other threads have ran to completion.
foreach (var t in threads)
t.Join();
}
}
类帐户
{
私有对象thisLock=新对象();
国际收支平衡;
随机r=新随机();
公共帐户(初始整数)
{
余额=初始值;
}
整数取款(整数金额)
{
//除非lock语句
//被注释掉了。
如果(余额<0)
{
抛出新异常(“负余额”);
}
//注释掉下一行以查看省略的效果
//锁定关键字。
锁(这个锁)
{
如果(余额>=金额)
{
控制台写线(“取款前余额:+余额”);
Console.WriteLine(“提取金额:-”+金额);
余额=余额-金额;
控制台写线(“提款后余额:+余额”);
退货金额;
}
其他的
{
返回0;//事务被拒绝
}
}
}
公共无效交易()
{
对于(int i=0;i<100;i++)
{
撤回(r.Next(1100));
}
}
}
课堂测试
{
静态void Main()
{
线程[]线程=新线程[10];
账户acc=新账户(1000);
对于(int i=0;i<10;i++)
{
螺纹t=新螺纹(新螺纹起点(acc.DoTransactions));
线程[i]=t;
}
对于(int i=0;i<10;i++)
{
线程[i].Start();
}
//阻塞主线程,直到所有其他线程运行完成。
foreach(线程中的变量t)
t、 Join();
}
}
我不明白为什么使用lock
账户余额不会变成负数;它总是以0
余额结束编译。
对不起,我的英语不好。
我不是100%肯定,但我假设MSDN提供的示例显示了类似生产者-消费者的模式。如果没有
lock
,多个线程处理同一数据(即balance
变量)可能会无意中出错lock
确保在任何给定时间只有一个线程可以读写balance。您正在生成十个线程,这些线程几乎同时启动并同时运行。与递减操作相比,控制台.WriteLine
调用非常耗时,因此如果没有锁
多个线程可以并且将进入由if(balance>=amount)
保护的语句块(因为到那时可能仍然有足够的余额)而其他任何线程都即将到达并执行balance=balance-amount
语句
(如果没有Console.WriteLine
调用,问题是相同的-争用的可能性可能更低,但是仍然需要lock
,除非关键部分是原子操作)
在锁定
到位的情况下,不会有两个线程同时进入关键部分,因此如果进入关键部分时余额已经为零,它将安全地分支到else
块,其中显示“事务已拒绝”
我认为,当您意识到这正是锁语法的目的时,您的误解就会得到解决——防止在需要序列化读和/或写访问的资源上出现竞争条件(这里,资源是平衡变量)。从这里开始,下一个挑战是
volatile
关键字,锁按照本例中的预期工作,在启动多个线程时,它们都会尝试从帐户中退出,这可能会导致非常糟糕的功能。怎么做?好吧,假设当前余额是40,线程1尝试提取40,线程2尝试提取20,如果没有锁,它们都会成功地这样做,并且余额是-20,这是不可接受的 现在你真正关心的是,为什么余额没有变为负值?简单:
lock (thisLock)
{
if (balance >= amount)
{
Console.WriteLine("Balance before Withdrawal : " + balance);
Console.WriteLine("Amount to Withdraw : -" + amount);
balance = balance - amount;
Console.WriteLine("Balance after Withdrawal : " + balance);
return amount;
}
else
{
return 0; // transaction rejected
}
}
锁将确保仅当余额中有可用金额时,每个线程才会退出,因此条件if(balance>=amount)
与if(balance<0)
相结合将确保余额不会变为负值
如果您记录每个线程提取的金额,您可以详细看到这一点:
Console.WriteLine(Withdraw(r.Next(1, 100)));
您将看到,很多帐户在一段时间后将输出0,因为帐户不再有余额,因此
返回0
会触发。但为什么它从不进入负数,每次都以0
帐户结束编译?关于任何给定时间的读/写毫无疑问clear@Sparkll看看锁中的条件,这使得你的答案不必要?除非你编辑它^^一切都很清楚,我问了一个愚蠢的问题。现在我仔细检查了代码,意识到我没有注意到如果(余额>=amount)
请记住随机
不是线程安全的,所以你也不能在锁之外使用它。值得注意的是,如果你为(int i=0;i<100;i++)
tofor(int i=0;i<2;i++)
您的程序以正平衡结束。绝大多数for
循环在计数到100时都是无操作的,因为平衡已经为零。