Skip to main content
GameDev.net gamedev.net
🔒 Locked

Windows Parallel Programming Question

Started by troxtril Jun 20, 2010 at 3:07 PM 8 replies 1.2k views
Original Post
troxtril
troxtril
I consider thread <-> thread function relation is same as main process <-> int main() function relation.

According to this definition (if it's right) can I do anything that I can do in main() with a thread function?

and with anything I mean; instantiating and destroying classes, making all sorts of Windows API calls, inline assembly including device control, initializing and using external libraries or dlls, creating and using opengl context and everything...

Burnhard
Burnhard
Yes and no. You can create things, make API calls, do your device control etc. but there will be times when you need to communicate with the main process and that is where the complexity is introduced. You generally can't just call a method on a class in use by the main process, if it changes state, without doing some kind of concurrency synchronisation.
troxtril
troxtril
Quote:
Original post by BurnhardYou generally can't just call a method on a class in use by the main process, if it changes state, without doing some kind of concurrency synchronisation.


is this because
a) it creates a copy in thread and you have to update it everytime the one in main process changed
or
b) it uses the same copy but main can change the state just after you make the method call in thread and this may cause inconsistent behaviour

or maybe anything else?
Burnhard
Burnhard
No, it's because you have various reader/writer concurrency problems if two threads are trying to access the same resource, i.e. one is writing it while the other is reading it. The reader cannot guarantee that he's read the right data unless there's some kind of guard that the writer has acquired and will release when he's finished (and the reader obviously takes up the guard when he starts reading). There are various concurrency primitives available, including critical sections, semaphores, etc. Moreover, if you do introduce locking, you may have deadlock, livelock and other such situations to deal with.

You should find some background information on the interwebs for all of these concepts. They aren't necessarily as straightforward as they may at first look :p.
KulSeran
KulSeran
Quote:

and with anything I mean; instantiating and destroying classes, making all sorts of Windows API calls, inline assembly including device control, initializing and using external libraries or dlls, creating and using opengl context and everything...

Err..... kinda.

As Burnhard is getting at, just about everything in memory can be accessed from any thread at any time. So you have to synchronize that access so that memory doesn't become inconstant. That means YOU have to write some special code for your own purposes, but you also have to consider the thread safety of any library or API call.

Some API calls can be made from any thread. There are a lot of ones that can't be though. Some APIs like OpenGL enforce that you can only call functions for a context from the thread that owns the context. Some API calls specifically enforce synchronization, so you can't call them in parallel. Some language features end up calling those functions, and thus force a sync. Calling "new" and "delete" for instance, will sync your threads slowing them down if there is a lot of parallel memory management.

Quote:

b) it uses the same copy but main can change the state just after you make the method call in thread and this may cause inconsistent behaviour

It's like this, only worse. The standard example is
int x = 2;---------------------thread a     thread bx++;         x++;---------------------cout << x;

What do you get? you either get 3 or 4 because "++" isn't an atomic operation. You could end up with
A: read 2
A: calc 2 + 1 => 3
B: read 2
B: calc 2 + 1 => 3
B: set x to 3
A: set x to 3

instead of the desired
B: read 2
B: calc 2 + 1 => 3
B: set x to 3
A: read 3
A: calc 3 + 1 => 4
A: set x to 4
Antheus
Antheus
Quote:
Original post by troxtril
and with anything I mean; instantiating and destroying classes, making all sorts of Windows API calls, inline assembly including device control, initializing and using external libraries or dlls, creating and using opengl context and everything...


No, none of that.

When using non-main thread, only those calls which are explicitly labeled as thread-safe, or list special conditions for using them from other threads may be called. Anything else can and will result in unexpected behavior.

OGL is single-threaded, and all calls must be made on appropriate thread. DLLs, while generally safe, bring along a lot of memory context issues, the library should be labeled as thread-safe or reentrant. Device control depends solely on API and various remarks with regard to specific implementation. Windows API calls state how/if they can be called from multiple threads. Allocating memory is allowed as long as the allocator is thread-safe. malloc and new generally are, but there can be hidden gotchas in containers, as well as any indirect allocation that occurs in third-party libraries.

So simple rule: No - unless having written permission to do so. The code will compile, compiler will not complain, but at runtime, obscure things will begin to occur, or application will randomly deadlock or even something stranger.
troxtril
troxtril
thanks for all the replies.

After last post of Antheus' I decided to read couple of thread safety papers to understand what makes libraries thread safe or thread unsafe. Nearly all of these documents talk about critical sections, mutex, locks, etc.

From this, I assume thread-safety is all about thread synchronization.

But what if a thread has nothing to do with another thread. What if it creates all the resource it needs, use it and destroy it without any interaction with other threads. Does this mean there is nothing called "thread-unsafe" library for such a model?
Burnhard
Burnhard
Can the thread do any useful work if it doesn't communicate with anything else? Probably. But the situations where this happens aren't as numerous as the situations where the thread is given work by another process (while it's running), or has to pass a result to another thread or process, or has to acquire read/write access to some shared resource.

I suppose you need to flesh out the context somewhat and tell us what you're thinking about using your threads for.
troxtril
troxtril
It's not about something in particular. I'm just trying to understand how it works. For example playing audio. This has nothing to with main thread except creation part.

You know, terms like "Thread-Safe" sound too "posh" and it makes someone who hasn't dealt with it before think "Hmm, Here is something advanced." but when you look at the inside story it's all about "two threads shouldn't access the same resource at the same time" :)
Fire Lancer
Fire Lancer
Well if you have a separate audio and main threads. You could use a library which is not thread-safe if only the audio thread uses it without having to have a global lock around it.

However you will still need synchronisation, since the audio and main thread will still need to communicate, for example the main thread might want another sound to be played, it might want to stop playback of all sounds, it might want to tell the audio thread to exit (program shutdown), change the volume, etc.

There are very few cases where a thread is just created and left until it terminates without interacting with other threads in the program.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.