This is the most simple example I can think of. It uses lock
. You can also use Monitor
. Basically I resolve a transient service 3 times. And then start a couple of threads which will increase the value of a shared service.
class Program
{
static async Task Main(string[] args)
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddSingleton<ValueService>();
serviceCollection.AddTransient<Service>();
var provider = serviceCollection.BuildServiceProvider();
var serviceOne = provider.GetRequiredService<Service>();
var serviceTwo = provider.GetRequiredService<Service>();
var serviceThree = provider.GetRequiredService<Service>();
// Manipulate the same object 1500 times, from different threads.
var task1 = serviceOne.DoStuff(500);
var task2 = serviceTwo.DoStuff(500);
var task3 = serviceThree.DoStuff(500);
// Wait for all the threads to complete.
await Task.WhenAll(task1, task2, task3);
// Verify the result.
var valueService = provider.GetRequiredService<ValueService>();
Console.WriteLine(valueService.SomeValue);
Console.ReadKey();
}
}
internal class Service
{
private readonly ValueService _service;
public Service(ValueService service)
{
_service = service;
}
public Task DoStuff(int noOfTimes)
{
var tasks = new Task[noOfTimes];
for (int i = 0; i < noOfTimes; i++)
{
tasks[i] = Task.Run(() =>
{
Thread.Sleep(100);
_service.Increase();
});
}
return Task.WhenAll(tasks);
}
}
internal class ValueService
{
public void Increase()
{
// Use lock to make sure that only one thread is changing the field at the time.
// Remove the lock statement and you will notice some "unwanted" behaviour.
lock (_lock)
{
SomeValue++;
}
// Alternatively you can use Interlocked.Increment(SomeValue)
}
private readonly object _lock = new object();
public int SomeValue { get; private set; }
}