In an application I have a ?DateTime
and an int
field I need to access (read/write) in multiple threads. I have read up on multiple resources, from which I learned:
- That some threads may use outdated values of variables
lock()
ing such a variable will solve this- The microsoft docs uses an example in which such a
lock()
is not used
From 1) and 3) I would conclude that Microsoft's example would suffer from potentially reading old data from balance
as the lock is done on balanceLock
.
A trimmed version of the example follows:
using System;
using System.Threading.Tasks;
public class Account
{
private readonly object balanceLock = new object();
private decimal balance;
public Account(decimal initialBalance) => balance = initialBalance;
public void Credit(decimal amount)
{
if (amount < 0)
{
throw new ArgumentOutOfRangeException(nameof(amount), "The credit amount cannot be negative.");
}
lock (balanceLock)
{
balance += amount;
}
}
public decimal GetBalance()
{
lock (balanceLock)
{
return balance;
}
}
}
class AccountTest
{
static async Task Main()
{
var account = new Account(1000);
var tasks = new Task[100];
for (int i = 0; i < tasks.Length; i++)
{
tasks[i] = Task.Run(() => Update(account));
}
await Task.WhenAll(tasks);
Console.WriteLine($"Account's balance is {account.GetBalance()}");
// Output:
// Account's balance is 5400
}
static void Update(Account account)
{
decimal[] amounts = { 0, 2, 3, 6, 2, 1, 8, 5, 11, 6 };
foreach (var amount in amounts)
{
account.Credit(amount);
}
}
}
I end up with the following questions:
- Is my conclusion correct that this example would suffer from data possibly not being guaranteerd to be up-to-date? I guess not, but then, why is it the case that the
balance
variable always contains an up-to-date value, regardless of which thread is working on it? (As it is thebalanceLock
variable that is being locked, notbalance
?) - If this is not ensured, what methods can I use to make sure values (e.g.
?DateTime
andint
) are always up to date between threads? I know aboutlock()
andvolatile
, but the former cannot be used with value fields, and it appearsvolatile
should only be used as a last resort.