• Advertisement

Archived

This topic is now archived and is closed to further replies.

Free Code (Easy Multithreading)

This topic is 5008 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I''ve been playing with threads, and I was able to produce an easy way to encapsulate thread making into an easy to use class. I will post the code and explain: Thread.h
#ifndef NETWORK_THREAD_H
#define NETWORK_THREAD_H

#pragma once

// Includes /////////////////////////////

#include <windows.h>

// Namespace ////////////////////////////

namespace Network
{

// Class ////////////////////////////////

class Thread
{
protected:
	HANDLE m_handle;
protected:
	friend void WINAPI ThreadFunc(Thread* _thread);
public:
	// virtual functions

	virtual void Create(LPSECURITY_ATTRIBUTES _security, SIZE_T _stackSize, DWORD _creationFlags); 
	virtual void Run() = 0;

	// Constructor/Destructor

	Thread(void);
	~Thread(void);
};

}

#endif // NETWORK_THREAD_H

This is the basic Thread class that all threaded classes will inherit from. There is a friend function named, ThreadFunc(), which accepts a pointer to a Thread as a parameter. The Create() function accepts parameters similar to the CreateThread() Win32 function. There is a pure virtual function named Run() which will be used to implement the threaded routine. The m_handle member data acts has a handle to the thread created by Create(). Thread.cpp
#include ".\thread.h"

namespace Network
{

Thread::Thread(void)
{
	m_handle = 0;
}

Thread::~Thread(void)
{
	this->Close();
}

void WINAPI ThreadFunc(Thread* thread)
{
	while(1)
	{
		thread->Run();
	}
}

void Thread::Create(LPSECURITY_ATTRIBUTES _security, SIZE_T _stackSize, DWORD _creationFlags)
{
	m_handle = CreateThread(_security, _stackSize, (LPTHREAD_START_ROUTINE)ThreadFunc, this, _creationFlags, 0);
}

void Thread::Close()
{
	if(m_handle)
	{
		CloseHandle(m_handle);
		m_handle = 0;
	}
}

}
Notice the definition of the ThreadFunc() function. It merely calls the pure virtual function Run() that will be defined by inherited classes. The Create() function is merely a wrapper for CreateThread(). The data members included are pretty standard, but notice the this pointer that is used as the parameter for the thread. This will be used to call the Run function of the inherited classes. ServerThread.h
#ifndef NETWORK_SERVERTHREAD_H
#define NETWORK_SERVERTHREAD_H

#pragma once

// Includes ///////////////////////////

#include <iostream>
#include "Thread.h"

// Namespace //////////////////////////

namespace Network
{

// Class //////////////////////////////

class ServerThread : public Thread 
{
public:
	int number;
public:
	// Thread Functions

	void Run(); 

	// Constructor/Destructor

	ServerThread(void);
	~ServerThread(void);
};

}

#endif // NEWORK_SERVERTHREAD_H

The ServerThread class inherited the Thread class. It only includes an int named number for demonstrational purposes. The Run() function must be included in the ServerThread class. Pretty simple stuff. ServerThread.cpp
#include ".\serverthread.h"

namespace Network
{

ServerThread::ServerThread(void)
{
}

ServerThread::~ServerThread(void)
{
}

void ServerThread::Run()
{
	std::cout << "(" << number << ")" << "Hi" << std::endl;
}

}
There you go. You only have to fill in the Run() function with whatever you want to be threaded. Main.cpp
// Includes ///////////////////////////////

#include <iostream>
#include "ServerThread.h"

// Main ///////////////////////////////////

int main()
{
	Network::ServerThread thread;
	Network::ServerThread thread2;

	thread.number = 1;
	thread2.number = 2;

	thread.Create(0, 0, 0);
	thread2.Create(0, 0, 0);

	while(1)
	{
		std::cout << "HI!" << std::endl;
	}

	return 0;
}
Here is a little sample main() function that demonstrates the threaded application. The program will switch between outputting "HI"!, "(1)Hi", and "(2)Hi". Though where and when it switches is random. It does not wait for one output statement to finish before switching threads. Though, when it comes back to the unfinished thread, it will start back where it left off.

Share this post


Link to post
Share on other sites
Advertisement
Actually, I need to make a change. Instead of declaring the base class Run() function as a pure virtual. It should be declared as just a virtual function in the Thread.h like this:

virtual void Run();

And be declared as this in the Thread.cpp file:

void Thread::Run()
{

}

I did not notice it before, but the destructers would be called for the inherited class while the thread was still being ran from the base class pointer. This would revert the friend function, ThreadFunc(), into trying to call the pure virtual function Run() in the base class. This would create an error. By making this change, then the ThreadFunc() will just call a blank function until the base class is destroyed.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
I think you should read a little bit more about virtuality...

The point of inheritance is to be able to call a specialized function ( from a derived class ) from a base class pointer...

Remember :

class shape
{
public:
virtual void draw() = 0;
}

class circle
{
public:
virtual void draw();
}

class square
{
public:
virtual void draw();
}

void main (void)
{
shape *a = new circle;
shape *b = new square;

a->draw();
b->draw();

delete (a);
delete (b);
}

Share this post


Link to post
Share on other sites
Spout your dribble elsewhere, Anonymous Poster. If you don''t know the problems associated with making a CreateThread wrapper, then don''t assume that I know nothing about virtual functions.

This is a workaround from the fact that because of the added "this" pointer associated with member class functions, they are not compatible with regular function pointers. This workaround allows a person, with the use of a BASE CLASS POINTER AND AN INHERITED VIRTUAL FUNCTION, bind a derived class member function to the thread. Not only that, but does it in a way that makes creating threads a no-brainer.

Share this post


Link to post
Share on other sites
I hope not to sounbd mean, but this is a very common way to encapsulate a Thread object. I wrote mine 2 years ago and it's quite the same than yours... I can't see another way to do it... By the way I guess that the MFC's CThread (even for some people not a reference) is doing the same in some way...

By the way if you're interested in some improvements... I can post mine...

[edited by - werbfred on June 8, 2004 6:50:47 AM]

Share this post


Link to post
Share on other sites
Why not make the Thread take either a member function or a free-standing function to call? Inheriting from a class to use it is laborous. This is what the AP was implying -- it is a bit of an OO abuse to inherit from Thread just to use the thing.

Additionally the ThreadProc could be hidden in the implementation file in an anonymous namespace. I don''t see the point of having a Create() member function -- it is just one thing for me to forget to call when the constructor or Run() could do it. There is no error checking on CreateThread() -- consider throwing an exception. Default arguments could be used to simplify normal uses of the code. _beginthreadex() should be used instead of CreateThread(), especially around C++. The instance pointer should be explicitly casted so it compiles fine on level 4 warnings.

And Stroustrup calls denoting blank parameter lists with void an abomination.

Thats all I can think of for now.

Share this post


Link to post
Share on other sites
Why don''t you use boost::thread and save yourself the hassle?

Share this post


Link to post
Share on other sites
I like the way Java does it. You can either inherit from Thread and define the run method, or create a Thread and give it a Runnable object to use. Then it''s up to the user to decide if they want to inherit or not.

As for the destruction problem, IMHO the user should be stopping the thread before destroying the thread object. Either in their derived class destructor or through a stop method.

Share this post


Link to post
Share on other sites
quote:
Original post by parla
Why don''t you use boost::thread and save yourself the hassle?


Yes, why dont we all just stop coding, and start using other people''s s**t?

argh, makes me mad when people instead of suporting you for doing the hard thing, slam you with "why didnt you use the ****"....

lame atitude...

Salsa cooked it, your eyes eat it!
[Hugo Ferreira][Positronic Dreams][Colibri 3D Engine][Entropy HL2 MOD][My DevDiary]
[Yann L.][Enginuity] [Penny Arcade] [MSDN][VS RoadMap][Humus][BSPs][UGP][NeHe]
"our stupidity allways comes back to bite us in the ass... in a white-shark sort of way..." - Prozak

Share this post


Link to post
Share on other sites
Yes, a very common way to do things, sorry nothing too original here, but an adequate job nonetheless.

Here is my threading library to give you other ideas: Click Here. It''s portable for Win32 and Posix, provides you with a base class for thread extension and provides a Simple Thread class that allows you to do one-shot threads that fire off a function where you don''t want to create a whole new class for it. It also provides a Simple Mutex class (again Win32/Posix flavours).

Any comments appreciated.

Regards,
Jeff


[ CodeDread ]

Share this post


Link to post
Share on other sites
By the way, I looked a little closer and I don''t like this:

void WINAPI ThreadFunc(Thread* thread){
while(1) {
thread->Run();
}
}

This means that your thread objects will loop forever. This is not the standard way of doing things...usually you just call thread->Run() and if the thread needs to loop do it inside teh Run() method (i.e. the implementation). the way you have it, there is no way to free up the thread context since it runs forever. Furthermore, you will crash your system when you try to delete the thread (suddenly your thread pointer is invalid).

You need some way of determining what happens if someone tries to delete a thread that''s still running (this will cause stack corruptions up the wazzoo!). This means you need to provide a getStatus() function in Thread and in the destructor if block until the status is "complete" or whatever.

Bzzz...back to the drawing board with that class.

Regards,
Jeff


[ CodeDread ]

Share this post


Link to post
Share on other sites
The usual way of doing things it to make ThreadFunc a static member (thus avoiding the "this" problems) instead of a global function. But otherwise it''s ok.

Share this post


Link to post
Share on other sites
quote:
Original post by Prozak
Yes, why dont we all just stop coding, and start using other people''s s**t?

argh, makes me mad when people instead of suporting you for doing the hard thing, slam you with "why didnt you use the ****"....


I''m only interested in end products, not how many wheels you reinvented along the way. He requested comments, and I provided quite a bit of constructive criticism.

Also:
Threads shouldn''t be in the Network namespace
There is no need to befriend the ThreadProc
The ThreadProc should have a catch-all exception handler to prevent exceptions from propagating to the OS

Share this post


Link to post
Share on other sites
quote:
Original post by antareus
quote:
Original post by Prozak
Yes, why dont we all just stop coding, and start using other people''s s**t?

argh, makes me mad when people instead of suporting you for doing the hard thing, slam you with "why didnt you use the ****"....


I''m only interested in end products, not how many wheels you reinvented along the way. He requested comments, and I provided quite a bit of constructive criticism.


*whoooosh*

Thats the sound of my whole point going over your head, so i''ll make it clear enough for you:
- Wheels NEED to be re-invented from time to time, even if not for only educational purposes of the programmer himself.

This doesnt mean i dont understand that in a work environment there is no time to redo much of this stuff, or that in your attemps it wont be as fast/eficient/neat-looking as other open-sourced libraries out there coded and reviewed by dozens of programmers...

...but its something any decent programmer, worthy of that job position, should eventually come to do, much like using pointers, dinamic memory allocation, etc...

Salsa cooked it, your eyes eat it!
[Hugo Ferreira][Positronic Dreams][Colibri 3D Engine][Entropy HL2 MOD][My DevDiary]
[Yann L.][Enginuity] [Penny Arcade] [MSDN][VS RoadMap][Humus][BSPs][UGP][NeHe]
"...if you had one shot, one oportunity, to seize everything you ever wanted for one moment, would you capture it? or just let it slip?" - Eminem

Share this post


Link to post
Share on other sites
Unfortunately the thread title is rather deceptive; having a C++ wrapper class for a thread is fine - I''m not knocking you for doing it, but it won''t make multithreading easy.

Because of course, the hard part about multithreading is correctly locking data so that you don''t get race conditions and data corruption (and at a sufficiently fine-grained level that you actually get any benefit from threading, and don''t spend all your time in synch. code)

Mark

Share this post


Link to post
Share on other sites

  • Advertisement