Simplified the question to give a more clear representation of what I'm actually asking
I have two threads, call them A
and B
. They share one object of type Foo
which has a field called Name
and is stored in an array of type Foo[]
at index 0
. The threads will always access index 0
in a order which is guaranteed by the system already, so there is no race condition of thread B
getting before thread A
.
The order is this.
// Thread A
array[0].Name = "Jason";
// Thread B
string theName = array[0].Name
As I said this order is already guaranteed, there is no way for thread B to read the value before thread A
What I want to ensure is two things:
B
always gets the latest value in the .Name field Marking Name
as volatile is not an option, as the real objects are a lot more complex and even have custom structs which can't even have the volatile attribute attached to them.
Now, satisfying 1 is easy (always getting the latest object), you can do a .VolatileRead:
// Thread A
Foo obj = (Foo)Thread.VolatileRead(ref array[0]);
obj.Name = "Jason";
// Thread B
Foo obj = (Foo)Thread.VolatileRead(ref array[0]);
string theName = obj.Name
Or you can insert a memory barrier:
// Thread A
array[0].Name = "Jason";
Thread.MemoryBarrier();
// Thread B
Thread.MemoryBarrier();
string theName = array[0].Name
So my question is: Is this enough to also satisfy condition 2? That I always get the latest value from the fields of the object I read out? If the object at index 0
has not changed, but the Name
has. Will doing a VolatileRead
or a MemoryBarrier
on index 0
make sure all the fields IN the object at index 0
also get their latest values?
None of these solutions, lock
or volatile
will solve your problem. Because:
volatile
ensures that variables changed by one thread are visible immediately to other threads operating on the same data (i.e. they are not cached) and also that operations on that variable are not reordered. Not really what you need.lock
ensures that the write / read do not occur simultaneously but does not guarantee their order. It depends which thread acquired the lock first, which is non-deterministic.Therefore, if your flow is:
Thread A read Name
Thread A modify Name
Thread B read Name
exactly in that order, you will need to enforce it with an event (i.e. AutoresetEvent
for example):
//Thread A
foo[0].Name = "John"; // write value
event.Set(); // signal B that write is completed
//Thread B
event.WaitOne(); // wait for signal
string name = foo[0].Name; // read value
This guarantees that thread B does not read the Name variable until A has modified it.
Edit: Ok, so you are sure that the above flow is respected. Since you are saying that you cannot declare the fields volatile
, I recommend the use of Thread.MemoryBarrier()
to introduce fences that enforce ordering:
//Thread A
foo[0].Name = "John"; // write value
Thread.MemoryBarrier();
//Thread B
Thread.MemoryBarrier();
string name = foo[0].Name; // read value
For more info, check this document: http://www.albahari.com/threading/part4.aspx
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With