【发布时间】:2020-12-30 18:32:42
【问题描述】:
我正在尝试一些与 C# 线程中的锁和互斥锁相关的概念。但是,如果发现使用 Mutex 给了我正确的结果,而使用 lock 则不一致。
使用lock 构造:
class BankAccount
{
private int balance;
public object padlock = new object();
public int Balance { get => balance; private set => balance = value; }
public void Deposit(int amount)
{
lock ( padlock )
{
balance += amount;
}
}
public void Withdraw(int amount)
{
lock ( padlock )
{
balance -= amount;
}
}
public void Transfer(BankAccount where, int amount)
{
lock ( padlock )
{
balance = balance - amount;
where.Balance = where.Balance + amount;
}
}
}
static void Main(string[] args)
{
var ba1 = new BankAccount();
var ba2 = new BankAccount();
var task = Task.Factory.StartNew(() =>
{
for ( int j = 0; j < 1000; ++j )
ba1.Deposit(100);
});
var task1 = Task.Factory.StartNew(() =>
{
for ( int j = 0; j < 1000; ++j )
ba2.Deposit(100);
});
var task2 = Task.Factory.StartNew(() =>
{
for ( int j = 0; j < 1000; ++j )
ba1.Transfer(ba2, 100);
});
Task.WaitAll(task, task1, task2);
Console.WriteLine($"Final balance is {ba1.Balance}.");
Console.WriteLine($"Final balance is {ba2.Balance}.");
Console.ReadLine();
}
代码为 ba2 提供了不正确的余额,而 ba1 设置为 0。
即使每个操作都被lock 语句包围,情况也是如此。它不能正常工作。
使用Mutex 构造:
class BankAccount
{
private int balance;
public int Balance { get => balance; private set => balance = value; }
public void Deposit(int amount)
{
balance += amount;
}
public void Withdraw(int amount)
{
balance -= amount;
}
public void Transfer(BankAccount where, int amount)
{
balance = balance - amount;
where.Balance = where.Balance + amount;
}
}
static void Main(string[] args)
{
var ba1 = new BankAccount();
var ba2 = new BankAccount();
var mutex1 = new Mutex();
var mutex2 = new Mutex();
var task = Task.Factory.StartNew(() =>
{
for ( int j = 0; j < 1000; ++j )
{
var lockTaken = mutex1.WaitOne();
try
{
ba1.Deposit(100);
}
finally
{
if ( lockTaken )
{
mutex1.ReleaseMutex();
}
}
}
});
var task1 = Task.Factory.StartNew(() =>
{
for ( int j = 0; j < 1000; ++j )
{
var lockTaken = mutex2.WaitOne();
try
{
ba2.Deposit(100);
}
finally
{
if ( lockTaken )
{
mutex2.ReleaseMutex();
}
}
}
});
var task2 = Task.Factory.StartNew(() =>
{
for ( int j = 0; j < 1000; ++j )
{
bool haveLock = Mutex.WaitAll(new[] { mutex1, mutex2 });
try
{
ba1.Transfer(ba2, 100);
}
finally
{
if ( haveLock )
{
mutex1.ReleaseMutex();
mutex2.ReleaseMutex();
}
}
}
});
Task.WaitAll(task, task1, task2);
Console.WriteLine($"Final balance is {ba1.Balance}.");
Console.WriteLine($"Final balance is {ba2.Balance}.");
Console.ReadLine();
}
通过这种方法,我每次运行时都能获得正确的余额。
我无法弄清楚为什么第一种方法不能正常工作。我是否遗漏了与 lock 语句相关的内容?
【问题讨论】:
-
基本上,除非我误读了某些内容,否则第一种方法不是线程安全的。第一种方法为
Transfer锁定ba1,而第二种方法同时锁定ba1和ba2,有效地禁止在处理ba1.Transfer时对ba2进行更改 -
@CamiloTerevinto 第一种方法为
Transfer锁定ba1并为Transfer锁定ba2。这些操作作为单独的任务运行。无法理解您为什么只说ba1被锁定为Transfer -
当您在第一种方法中调用
ba1.Transfer(ba2, 100);时,锁定是针对ba1.padlock,not 针对ba2.padlock(并且这个仍然未锁定)跨度> -
@CamiloTerevinto 你的cmets对我来说很清楚。谢谢
-
我可能会误解一些东西,所以我会等到有更多锁具经验的人回答:)
标签: c# multithreading locking task mutex