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
balancevariable always contains an up-to-date value, regardless of which thread is working on it? (As it is thebalanceLockvariable that is being locked, notbalance?) - If this is not ensured, what methods can I use to make sure values (e.g.
?DateTimeandint) are always up to date between threads? I know aboutlock()andvolatile, but the former cannot be used with value fields, and it appearsvolatileshould only be used as a last resort.