Threading Design

Started by
5 comments, last by Bregma 17 years ago
Hello, I'm someone that's always looking for the perfect design for an application. I can't stand it when someone is creating ugly code so I'm always using coding guidelines for my projects. One of the rules is: Don't use global variables unless there isn't any other way. With this in mind; hear my problem ;) I want to create a multithreaded game in C++. Some code:

#include "stdafx.h"
#include "Thread1.h"
#include "Thread2.h"

int main(int argc, char *argv[])
{
	Thread1 *Thrd1 = new Thread1(); // This could be our render thread...
	Thread2 *Thrd2 = new Thread2(); // And this could be our drink some beer thread...

	Thrd1->Start();
	Thrd2->Start();

	delete Thrd1;
	delete Thrd2;

	return 0;
}

Just a little application which is starting two threads and will execute them. Now these threads both have a run method where they're printing text to the screen (for example). This of course, needs be synchronized.
// Thread1
while (IsRunning)
{
	Lock->Acquire();
	std::cout << "Written from thread 1" << std::endl;
	Lock->Release();
}

// Thread2
while (IsRunning)
{
	Lock->Acquire();
	std::cout << "Written from thread 2" << std::endl;
	Lock->Release();
}


As you see is the lock used in both threads... hey this is in conflict with my coding guidelines ;) So my question: How can I use multithreading in an elegant way without the use of global variables.
Advertisement
Same way you would in any other application, pass along Lock as a parameter (or member of a parameter) to both Thread objects.
I don't think there is such a thing as an elegant implementation of multithreading for anything nontrivial - or if there is, I haven't seen it (possibly Eiffel?). Then it becomes orders of magnitude harder (or so it seems, anyway) when you're not just dealing with multiple threads with multiple cores (because two cores might try to access the same data at the same time).

However, if you're acquiring a lock (I presume you're doing so atomically, of course - otherwise you simply circumvent any protection which the lock offers you) then you're not acquiring a global lock - you're acquiring a lock on a single object - so associate a lock with that object (as a member?) rather than having a single global variable.

Or perhaps there's something more complex that I'm missing...
[TheUnbeliever]
Quote:Original post by Telastyn
Same way you would in any other application, pass along Lock as a parameter (or member of a parameter) to both Thread objects.


Or encapsulate the lock as part of the resource that multiple threads can access. Why are you (the original poster) sharing the std::cout global across threads anyway? ;)
Thnx for the answers. Hmm just passing the lock as a parameter is actually not a bad idea :)

Quote:However, if you're acquiring a lock (I presume you're doing so atomically, of course - otherwise you simply circumvent any protection which the lock offers you) then you're not acquiring a global lock - you're acquiring a lock on a single object - so associate a lock with that object (as a member?) rather than having a single global variable.

Or perhaps there's something more complex that I'm missing...


Uhm that Lock can be anything but let's just assume it's a class which holds a handle of a mutex.

Quote:Why are you sharing the std::cout global across threads anyway? ;)
It's just an example ;)

Arn't there any good articles on how to write good looking code?
Quote:Original post by WalterTamboer
Quote:However, if you're acquiring a lock (I presume you're doing so atomically, of course - otherwise you simply circumvent any protection which the lock offers you) then you're not acquiring a global lock - you're acquiring a lock on a single object - so associate a lock with that object (as a member?) rather than having a single global variable.

Or perhaps there's something more complex that I'm missing...


Uhm that Lock can be anything but let's just assume it's a class which holds a handle of a mutex.


I don't follow as to how this voids my suggestion (or indeed Kylotan's, since that is what I mean). Just define all 'Lock' types (mutexes, semaphores - whatever) to have a basic interface (or perhaps even define a larger class of synchronisation procedures) from which they inherit so that you can take advantage of scope for polymorphism and then still have them as a member - which you can then call the Lock->Acquire method on. This way, the lock for the object is associated with the object, and is not a separate entity (which, really, it's not) - also has the benefit of being in scope whenever the object being used is in scope.
[TheUnbeliever]
Quote:Original post by WalterTamboer
Quote:Why are you sharing the std::cout global across threads anyway? ;)
It's just an example ;)

It's not just an example. It's the answer to your question. Think about it.

--smw

Stephen M. Webb
Professional Free Software Developer

This topic is closed to new replies.

Advertisement