More Threading

Started by
6 comments, last by WebsiteWill 20 years, 8 months ago
OK I have my program spawning threads as usual but for some reason it seems to be only executing one of the threads (no matter how many I spawn. The thread function is similar to this at this point.

LocalSend()
{	
	while(bKeepAlive == true)
	{	
		cout << "In LocalSend()";
		cout << GetCurrentProcessId() << endl;
		Sleep(1000);
}
LocalRecv()
{	
	while(bKeepAlive == true)
	{	
		cout << "In LocalSend()";
		cout << GetCurrentProcessId() << endl;
		Sleep(1000);
}
The main program defines and object and passes in the number of each thread to create. The threads are getting created correctly with unique HANDLES/threadIDs. I check the handles just after I create the threads and before pushing the handles onto a vector for keeping. However, when I execute the code it keeps outputting the HANDLE/ID of the same thread over and over. Here is how I am creating my threads:

StartThreads(int iNumRecvThreads, int iNumSendThreads)
{
        int iThreadCount;
	HANDLE hThreadHandle;
	DWORD dwThreadID;

	//cBaseServer* pRecv = NULL;

	//cBaseServer* pSend = NULL;


	for (iThreadCount=0; iThreadCount < iNumRecvThreads;
             iThreadCount++)
	{
	    hThreadHandle = CreateThread(NULL, NULL,
                            RecvDispatch,
  			    reinterpret_cast< LPVOID >(this),
			    NULL, &dwThreadID);
	    if (hThreadHandle == NULL)
	    {
                perror("Recieve Thread Creation Problem");
		exit(1);
	    }
	    vRecvThreadPool.push_back(hThreadHandle);
	}

	for (iThreadCount=0; iThreadCount < iNumSendThreads;
             iThreadCount++)
	{
	    hThreadHandle = CreateThread(NULL, NULL,
                           SendDispatch,						   reinterpret_cast< LPVOID >(this),				   NULL, &dwThreadID);
	    if (hThreadHandle == NULL)
            {
	        perror("Recieve Thread Creation Problem");
	        exit(1);
	    }
            vSendThreadPool.push_back(hThreadHandle);
        }
}
I might be mistaken but isn''t Sleep(xxx) supposed to put that specific thread to sleep for the given milliseconds thus allowing other threads to use the processor? It''s something trivial I know, but I can''t pick it out Thanks for any help, Webby
Advertisement
GetProcessId returns the process id, which is the same for all threads in the process. Try GetCurrentThreadId.
John BoltonLocomotive Games (THQ)Current Project: Destroy All Humans (Wii). IN STORES NOW!
> cout << GetCurrentProcessId() << endl;

Try "GetCurrentThreadId()" instead. The process ID remains the same regardless of the threads it contains. This may not hold true under some Unix flavor, though.

> vSendThreadPool.push_back(hThreadHandle);

I don't know how you handle this vector, but it would be better to protect it if the threads use it.


I'm not too comfy with getting multiple threads in a single C++ object. Those threads should be objects on their own and communicate with the main object with callbacks (control/notify paradigm). Here's an example from one of my libs:

class ILibnetNotify{	public:		virtual	void	ErrorMessage( int, unsigned long, const char * ) = 0;		virtual	void	IncomingPacket( int, void *, int, int ) = 0;		virtual	bool	KeepThreadAlive( int ) = 0;};class MyApp : public ILibnetNotify{	public:		MyApp();		~MyApp();	public:		//---- ILibnetNotify interface		void	IncomingPacket( int, unsigned long, const char * );		void	ErrorMessage( int, unsigned long, const char * );		bool	KeepThreadAlive( int ;	private:		int			m_iPrivate_int;		ILibnetControl *	m_pServerControl[XXX];};


And the main object MyApp creates the threaded object using the calls:

LIBNET_CreateInstance( & m_pServerControl );<br>m_pServerControl->Initialize( i,(ILibnetNotify*)this );<br><br>where 'i' can serve as a thread index in your array (also communicated to the thread's object). The thread object is controlled via the ILibnetControl interface (not listed here), and the object notifies its owner via the ILibnetNotify interface. Note the 'int' in every function so that the main object can recognize from which thread the notification comes from, such as the KeepThreadAlive() call to check if the thread needs to exit.<br><br>-cb <br> <br><br><SPAN CLASS=editedby>[edited by - cbenoi1 on August 11, 2003 1:19:04 PM]</SPAN>
Ahhhh GetCurrentThreadId(), that''s the ticket.

Thanks guys. The way I am doing it inside of the class is working quite nicely and is very compact and quite elegant IMO. But of course I have to be biased towards my own code don''t I?

I''m just learning as I go here and I must say it has been a heck of a learning experience so far.

Hopefully I''ll get to share the results with you all when it''s done. Maybe in the form of a nice tutorial or something. At that time I think my overall reasoning for this setup will become more clear. Then again, maybe once I start stress testing it, it will fall all the pieces

By the way, the queue is getting protected with critical sections. I lock it when I need and release it when I don''t. Basically, each thread does 2 things. 1) Check the queue for a packet and pop it off if necessary and 2) send the packet over the network. This requires me to lock data in two particular places. However, at the same time, it should allow me to begin servicing a second packet as soon as the first thread is done with the queue and is waiting to be sent. That''s the theory anyway.

The end code will hopefully be very robust as I begin adding checks for all necessary error conditions and such. With any luck and more help from you fine fellows (and possible ladies out there?) this will turn out quite nice.

Aside from that, it''s a generic object all to itself. If you don''t want a threaded server it''s a simple matter of initializing the network object that way and it knows what to do from there. All the user needs to do is create an instance of it with the desired parameters and then only process the packets it gives you and push on packets to it for sending. Everything else is done behind the scenes and the user hopefully will never know it''s even there.

The object uses no game code whatsoever so in theory it can be used in pretty much any application. In theory

Thanks all,
Webby
Devil''s advocate here.

Thread for each connection? Sure it makes it easy, but, you can do it all asynchronously in one thread and not have to deal with synchronization issues.
--God has paid us the intolerable compliment of loving us, in the deepest, most tragic, most inexorable sense.- C.S. Lewis
Nope, not a thread for each connection.
1 (or more threads) servicing all incoming packets.
1 (or more threads) servicing all outgoing packets.

Not a new thread for every packet that comes in. Basically just a resizable thread pool. At worst case it breaks down to exactly what you described (given those parameters) or it scales up to take advantage of multiple processor machines (should it be so lucky).
Lookup I/O Completion Port might helps.
"after many years of singularity, i'm still searching on the event horizon"

Actually, what I am creating is a really basic version of IOCP that will work on Unix.

I know it sounds funny because I''ve been asking about CreateThread and other windows functions. That''s because I''m doing the grunt work in MSVC++ where I am comfortable. When it comes time to move over it''s just a matter of changing CreateThread() with p_thread_create() or whatever the Unix version is called and possibly altering a parameter or so.

For the sockets part it''s a matter of taking out windows include files, putting in basic sockets includes and removing the calls to WSAStartup and the close sockets function. Nothing major at all.

Your point is well taken though, if I was developing this for Windows I most certainly would be using IOCP.

Webby

This topic is closed to new replies.

Advertisement