View Full Version : Threading tutorials
Fenris
2005.05.12, 03:55 AM
Does anyone know of any good tutorials/articles on getting started with threading? Preferrably boost::threads, but seeing as the supply is scarce on it, pthreads would be just fine. (Or whatever threading lib you might've found.)
OneSadCookie
2005.05.12, 04:46 AM
Threading itself is remarkably simple -- all the APIs are pretty much the same. You get some way to call a function in a new thread, and some synchronization APIs, the details which can differ significantly depending on which threading API you use, but ultimately perform the same task.
The crucial thing to make sure you understand is why this function is buggy:
int x = 0;
void add_one_to_x()
{
++x;
}
Once you understand that, the rest is basically API details.
Fenris
2005.05.12, 08:44 AM
Well, if you say so. :) I'll start with pthreads then, and move to boost when I feel I can handle posix. As to why the function is buggy, idk... because the data is shared between threads and I can't trust x to be incremented by one by calling it? It might be incremented from any number of threads, right under my nose?
Puzzler183
2005.05.12, 09:04 AM
x isn't volatile, therefore the value in it could be wrong?
TomorrowPlusX
2005.05.12, 03:30 PM
This brings up a big question for me. I downloaded OSC's threaded-newton demo, hoping to glean some useful info. But it seemed... a little short on documentation as to how it works. If I recall, I didn't see any comments, really, and no overview of what it's doing.
I could spend a couple hours figuring out what it's doing, but I might as well ask the author: OSC -- what's it doing? What's the principle of its operation?
Now, that said, I've contemplated this for my own work. My game currently is multithreaded, but in a synchronous manner. E.g., the physics thread runs while the drawing thread blocks for VBL sync. So, really, it's only multithreaded in name, not behavior. Also, I'm not using Newton, I'm using ODE. So what works for Newton may not be applicable elsewhere -- I don't know, since I don't know anything about newton other than that it looks like it ROCKS.
The way I see a generic multithreaded display & physics system working, is this: An object which is to have physics ( and other game logic ) run in one thread, and drawn in another, has to have a sort of contract, that the physics thread provides certain info for the drawing thread. Producer, consumer. Info like position, rotation, and or color or whatever, I'll call that "state". The drawing thread simply represents the object with the state the physics thread provides.
I would have each object in the game have two state objects, lets call them primary and secondary, primary has a mutex lock on it. The physics/state thread runs constantly of course and locks the mutex on the primary state, updates it, and unlocks it. The drawing thread first attempts to get a lock on the primary state to display the object with the most recent state. If it succeeds, it draws the object with that state and then copies the state to the secondary state (which the physics thread never touches). Then it unlocks the primary state. If it can't get a lock on the primary state, it just draws whatever was in the secondary state, even though it will be a little stale.
Obviously, there would need to be a first-pass situation where the physics thread locks both states and copies the first to the second, once. And there might be need to, say, every half second or so, require the drawing thread to block on gaining the primary state so it can be sure to have a not-too-stale fallback state.
But, anyway, is this dumb, or is it particularly inefficient? What do people here say? I've never attempted this, and I'm up for some constructive criticism of my approach.
OneSadCookie
2005.05.12, 06:24 PM
Puzzler183, yes, the addition of volatile would be an improvement. However, the function would still be buggy.
Here's the assembler output from that function:
_add_one_to_x:
00000000 mfspr r0,lr
00000004 bcl 20,31,0x8
00000008 mfspr r10,lr
0000000c mtspr lr,r0
00000010 addis r9,r10,ha16(_x-0x8)
00000014 lwz r2,lo16(_x-0x8)(r9)
00000018 addi r2,r2,0x1
0000001c stw r2,lo16(_x-0x8)(r9)
00000020 blr
Most of it's function prologue and epilogue, these three instructions do the grunt-work of the function:
00000014 lwz r2,lo16(_x-0x8)(r9)
00000018 addi r2,r2,0x1
0000001c stw r2,lo16(_x-0x8)(r9)
lwz loads a value from memory into a register (r2, from x, in this case)
addi adds an "immediate" (stored-in-the-instruction) value to a value in a register (1, to r2, putting the result in r2)
stw stores a value from a register into memory (r2, into x)
Now, there's nothing at all wrong with this without threading, or if you're careful to make sure that two threads don't call add_one_to_x simultaneously, but if they do, there are possible orderings of the ASM instructions that cause the wrong thing to happen, for example
thread 1: lwz (x == 0, r2 == 0)
thread 1: addi (x == 0, r2 == 1)
thread 2: lwz (x == 0, r2 == 0)
thread 2: addi (x == 0, r2 == 1)
thread 2: stw (x == 1, r2 == 1)
thread 1: stw (x == 1, r2 == 1)
add_one_to_x has been executed once in each thread, and the value of x has only been incremented by 1.
OneSadCookie
2005.05.12, 06:32 PM
TomorrowPlusX, I believe that what you describe will still force the two threads to run at the same framerate, and you'll waste a lot of time waiting for the slower thread to relinquish its lock on one of the two buffers. Also, mutexes are very heavyweight objects, and you can easily lose all the benefits of multithreading to the overheads of synchronization if you're not careful. I'll describe how the Newton demo works in its thread (http://www.idevgames.com/forum/showthread.php?t=8968)
TomorrowPlusX
2005.05.12, 08:36 PM
Thanks, OSC. I'll read your writeup.
vBulletin® v3.6.8, Copyright ©2000-2008, Jelsoft Enterprises Ltd.