Reputation: 2572
I'm running into a scenario where I'm not sure why am I not reading the correct property value from a property while in multiple threads.. I have written a small console app to illustrate the situation
class Program
{
private static Test MyObject;
static void Main(string[] args)
{
MyObject = new Test();
Task.Run(() =>
{
var obj = MyObject as Test;
Console.WriteLine("1: " + obj.MyValue);
Update("Thread1");
Console.WriteLine("2: " + obj.MyValue);
});
Task.Run(() =>
{
var obj = MyObject as Test;
Console.WriteLine("3: " + obj.MyValue);
Thread.Sleep(100);
Update("Thread2");
Console.WriteLine("4: " + obj.MyValue);
});
void Update(string val)
{
lock (MyObject)
{
MyObject.MyValue = val;
}
}
Thread.Sleep(400);
Console.WriteLine("5: " + MyObject.MyValue);
}
}
class Test
{
public string MyValue { get; set; }
}
Running the above line I'm getting this output
1:
2: Thread1
3:
4: Thread2
5: Thread2
My expectation is "3:" should always say "3: Thread1" because same object was updated earlier. But this is not the case and I'm not sure what am I missing here... Can someone shed some light on this please?
Just a small note... if you run the code and get different order, please re-run it... I'm only interested in order like above.
Upvotes: 1
Views: 91
Reputation: 70662
A couple of things:
Update()
method synchronizes writes to the MyValue
property, but does nothing about reads. So, the runtime is free to use a cached value for the object. It's unlikely this has anything to do with your observed output, since in practice you're not likely to see this, especially on x86 hardware. But…Console
class has synchronization to ensure coherent output from multiple threads, but there's nothing in the code that ensures that if lines of output are written in a specific order, that the code leading up to those lines of output (such as reading the MyValue
property) occurred in the same order that those lines of output are displayed.In other words, just because the console displayed the "2:"
line before the "3:"
line, that doesn't actually mean that the call to Update("Thread1")
occurred before the call to Console.WriteLine("3: " + obj.MyValue);
You would need to protect the individual operations with a lock
statement as well, if you wanted to ensure the lines of output matched the order of execution of statements in the program.
To be more specific, consider the following possible order of execution for your code:
thread 1 thread 2 -------- -------- "value" parameter <= "3: " + obj.MyValue Console.WriteLine("1: " + obj.MyValue); Update("Thread1"); Console.WriteLine("2: " + obj.MyValue); Console.WriteLine(value);
I.e. it's perfectly legal for the second thread to compute the parameter passed to Console.WriteLine()
before the first thread gets around to even starting its logic. But then the first thread can also preempt the second thread before the second thread has a chance to actually call the Console.WriteLine()
method.
If that happens, then you see the expected order of output from thread 1, but the output from thread 2 writes an apparently-stale version of the MyValue
property, because it was retrieved before thread 1 got to run any of its logic.
To address that specific scenario, you could use lock
for the WriteLine()
calls. E.g.:
lock (MyObject) Console.WriteLine("1: " + obj.MyValue);
Note that you'd need to put that on every call to Console.WriteLine()
. That would ensure that whatever value is written to the console is the most up-to-date value for the property at the time the call was made.
Upvotes: 3