Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write thread-safe C# code for Unity3D?

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?

  1. Is it possible to solve by, only applying volatile keyword.

  2. Or is it possible to read and write value through Interlocked's methods like Exchange and Read?

  3. If I surround _done read and write operation with lock (_someObject), need I use Interlocked or something to prevent caching?

Edit 1

  1. If I define _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?
like image 484
Stas BZ Avatar asked Apr 07 '17 09:04

Stas BZ


People also ask

What is thread-safe code in C?

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.

Is printf () thread-safe?

the standard C printf() and scanf() functions use stdio so they are thread-safe.

Is ++ thread-safe in C?

++ is not defined as thread-safe.


2 Answers

  1. 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?

  2. 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

  3. 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.

like image 187
Marc Gravell Avatar answered Oct 17 '22 02:10

Marc Gravell


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.

like image 11
Fabjan Avatar answered Oct 17 '22 03:10

Fabjan