I'd like to understand how to write thread safe code.
For example I have this code in my game:
bool _done = false;
Thread _thread;
// main game update loop
Update()
{
// if computation done handle it then start again
if(_done)
{
// .. handle it ...
_done = false;
_thread = new Thread(Work);
_thread.Start();
}
}
void Work()
{
// ... massive computation
_done = true;
}
If I understand it correctly, it may happened that main game thread and my _thread
can have its own cached version of _done
, and one thread may never see that _done
changed in another thread?
And if it may, how to solve it?
Is it possible to solve by, only applying volatile
keyword.
Or is it possible to read and write value through Interlocked
's methods like Exchange
and Read
?
If I surround _done
read and write operation with lock (_someObject)
, need I use Interlocked
or something to prevent caching?
Edit 1
_done
as volatile
and call Update
method from multiple threads. Is it possible that 2 threads will enter if
statement before I assign _done
to false?A threadsafe function protects shared resources from concurrent access by locks. Thread safety concerns only the implementation of a function and does not affect its external interface.
the standard C printf() and scanf() functions use stdio so they are thread-safe.
++ is not defined as thread-safe.
yes, but technically that's not what the volatile
keyword does; it has that result as a side-effect, though - and most uses of volatile
are for that side-effect; actually the MSDN documentation of volatile
now only lists this side-effect scenario (link) - I guess the actual original wording (about reordering instructions) was just too confusing? so maybe this is now the official usage?
there are no bool
methods for Interlocked
; you'd need to use an int
with values like 0
/1
, but that's pretty much what a bool
is anyway - note that Thread.VolatileRead
would also work
lock
has a full fence; you don't need any additional constructs there, the lock
by itself is enough for the JIT to understand what you need
Personally, I'd use the volatile
. You've conveniently listed your 1/2/3 in increasing overhead order. volatile
will be the cheapest option here.
Although you might use volatile
keyword for your bool flag, it does not always guarantee a thread-safe access to the field.
In your case I'd probably create a separate class Worker
and use events to notify when background task completes execution:
// Change this class to contain whatever data you need
public class MyEventArgs
{
public string Data { get; set; }
}
public class Worker
{
public event EventHandler<MyEventArgs> WorkComplete = delegate { };
private readonly object _locker = new object();
public void Start()
{
new Thread(DoWork).Start();
}
void DoWork()
{
// add a 'lock' here if this shouldn't be run in parallel
Thread.Sleep(5000); // ... massive computation
WorkComplete(this, null); // pass the result of computations with MyEventArgs
}
}
class MyClass
{
private readonly Worker _worker = new Worker();
public MyClass()
{
_worker.WorkComplete += OnWorkComplete;
}
private void OnWorkComplete(object sender, MyEventArgs eventArgs)
{
// Do something with result here
}
private void Update()
{
_worker.Start();
}
}
Feel free to change the code according to your needs
P.S.
Volatile is good performance-wise, and in your scenario it should work as it looks like you get your reads and writes in the right order. Possibly the memory barrier is achieved precisely by reading/writing freshly - but there is no guarantee by MSDN specifications. It's up to you to decide whether to take the risk of using volatile
or not.
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