I was trying out some concepts related to lock and Mutex in C# Threading. However if found that using Mutex gave me correct results while that by using lock were inconsitent.
With lock
construct:
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();
}
The code was giving incorrect balance for ba2
while ba1
was set to 0.
This is the case even though each operation is surrounded by lock
statement. It is not working correctly.
With Mutex
construct:
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();
}
With this approach I was getting correct balances every time I ran it.
I am not able to figure out why first approach is not working correctly. Am I missing something with respect to lock statements?