What little information you gave us, sounds like there is quite a bit of spaghetti code going on across many different threads. I'm afraid you will have to seriously rethink how you organise your multi-threaded application.
I recommend looking at concepts used by the
Actor Programming Model. Basically you have a bunch of self contained objects, called actors, that are tasked to perform a very specific duty. One might be the GUI handler, the other might be GPU scheduler, another that does networking. Each actor runs its own thread, and you can safely pass data between actors via a messaging system. Actors consume messages from a queue and serially executes them internally and sends results back to other actors (if appropriate) via each other's messaging system. You don't have to strictly adhere to the Actor Model, but thinking along the lines of each thread being a black box, or a completely self-contained system, where you copy data back and forth, is a good start for multi-threaded programming.
Few other tips with respects to multi-threaded programming:
* Data that was created by a thread, should be exclusively owned by that thread, and never shared.
* When passing data between threads,
move the object from one thread to another (i.e. transfer ownership) instead of sharing.
* If you can't transfer ownership,
deep copy. This may sound expensive, but trust me, copying saves a lot of headaches and simplifies your APIs. And you might be surprised how performant copying can be.
* If you can't copy, then share data that is
immutable to all threads touching it, and manage its lifetime with an atomic reference counter.
* Try double/triple buffering if threads need to simultaneously read/write data. Atomically swap pointers.
* If you absolutely must share a mutable object, use an atomic data type. If that's not possible, wrap it up with a mutex. This should be absolutely your last resort. Don't be tempted to wrap everything in a mutex and call it a day.
* Never write your own spinlock algorithms. All you are doing is "
Sheldon knocking" at an atomic flag, burn electricity, and introduce priority inversions.
* Try using completion handlers for asynchronous tasks, rather than awaiting for a future object.
* Use co-routines if you have such features available.
* Copy, copy, copy.