Reputation: 311
When doing thousands of iterations of the same function when using Task.Run, the end result when tracking how many times a value has been incremented results in the total iterations don't match up with how many times the value has been incremented. Why? How to fix? Suggested alternative ways? My goal is simply to be able to run the same function in a multithreaded environment as fast as possible.
Code example below shows what I am trying now, you should be able to run it as is to see similar results as I am seeing:
taskNumber = 9980, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 9998, nonTaskIterations = 10000
taskNumber = 9998, nonTaskIterations = 10000
taskNumber = 9999, nonTaskIterations = 10000
taskNumber = 9997, nonTaskIterations = 10000
taskNumber = 9999, nonTaskIterations = 10000
taskNumber = 9997, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 9995, nonTaskIterations = 10000
taskNumber = 9994, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 9998, nonTaskIterations = 10000
taskNumber = 9999, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 9999, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
static void Main(string[] args)
{
for (int i = 0; i < 20; i++)
{
//iterate many times to see different outputs
PrimaryFunction();
}
Console.WriteLine("why the hell does TaskNumber not always == nonTaskIterations value?");
Console.ReadLine();
}
static void PrimaryFunction()
{
long taskNumber = 0;
int nonTaskIterations = 0;
for (int i = 0; i < 100; i++)
{
List<Task> tasks = new List<Task>();
for (int j = 0; j < 100; j++)
{
nonTaskIterations++;
tasks.Add(Task.Run(() =>
{
taskNumber = taskNumber + 1;
DoWork(taskNumber);
}));
}
Task.WaitAll(tasks.ToArray());
}
Console.WriteLine("taskNumber = " + taskNumber + ", nonTaskIterations = " + nonTaskIterations);
}
static void DoWork(long i)
{
//do work here
Random random = new Random();
var x = random.Next(0, 10).ToString();
}
I would expect to see this every time it is run:
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
I am open to suggestions on how to fix this or alternative ways that are also fast and efficient, but also accurate, to run a multi threaded environment function over and over thousands of times without missing any incremented values.
Upvotes: 3
Views: 424
Reputation: 81513
This is simply not thread safe (and I am sure you have come to that conclusion yourself). You can't expect multiple threads on multiple cores to always have the same value in cache
One way to get the values you expect is to use Interlocked.Increment which acts as a memory barrier eliminating the processor's ability to re-order memory accesses around the instruction and in-turn will make sure the increment operations are atomic
Increments a specified variable and stores the result, as an atomic operation.
Interlocked.Increment(ref someValue);
Output
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
taskNumber = 10000, nonTaskIterations = 10000
Upvotes: 3