Threads: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
No edit summary
No edit summary
Line 5: Line 5:
== Threads in Source ==
== Threads in Source ==


Thread in Source are child classes of <code>CWorkerThread </code>
Threads in Source are child classes of <code>CWorkerThread</code>. These contain a number of variables, a calling command, and the mainloop <code>int Run()</code>. An instance of the class is declared inside a source file. The other threads can call this thread by calling the proper functions in this instance. The thread is not nessesarily running when these functions are executed and will most likely be required to be started prior to any calls. A sample thread with a single function would look like this.
 
<pre>
//-----------------------------------------------------------------------------
// A thread for async PingBack'ing.
//-----------------------------------------------------------------------------
class CMyAsyncThread: public CWorkerThread
{
public:
  CAsyncPingBackThread() :
  m_Parameter1( NULL ) ,
  m_Parameter2( NULL )
  {
  SetName( "MyAsyncThread" );
  }
 
  ~CAsyncPingBackThread()
  {
  }
 
  enum
  {
  CALL_FUNC,
  EXIT,
  };
 
  bool CallThreadFunction( char* Parameter1, char* Parameter2 )
  {
  Assert( !Parameter1);
  Assert( !Parameter2 );
  m_Function = Function;
  m_Parameter = Parameter;
  CallWorker( CALL_FUNC );
  Assert( !Parameter1);
  Assert( !Parameter2 );
  return true;
  }
 
  int Run()
  {
  unsigned nCall;
  while ( WaitForCall( &nCall ) )
  {
  if ( nCall == EXIT )
  {
  Reply( 1 );
  break;
  }
 
  // Reset some variables
  char* Parameter1 = m_Parameter2;
  char* Parameter2 = m_Parameter1;
  m_Parameter1 = 0;
  m_Parameter2 = 0;
 
  Reply( 1 );
  FunctionToBeRunFromInsideTheThread(Parameter1,Parameter2);
 
  }
  return 0;
  }
 
private:
char* m_Parameter1;
char* m_Parameter2;
};
 
static CMyAsyncThread g_CMyAsyncThread;
</pre>


<pre>
<pre>

Revision as of 16:26, 11 December 2008

Modern computers utilize multiple processors and recently games have begun to take advantage from this. By splitting code into multiple threads, it's possible to run code simutaniously on multiple processors. Threads are a very important part of Windows, where most programs use bunches of threads. Please note that the number of threads available is not limited by the number of CPUs, but by the memory in windows. Each thread will by default have a stack size of one megabyte, therefore Win 32 has a limitation of 2048 threads. This article is not about threads in general, but about threads in Source, and assumes you already know how threads work. For more information on how threads work in Windows please read about it on another site.

Source utilizes threads by putting each kind of operation into its own thread. Source has for instance a thread for physics, a thread for AI, a thread for rendering, and so on. If the computer does not have multiple CPUs, it will run every thread on the same CPU and switch between them every few miliseconds. Programmers using the Source SDK can use threads as well by using Valve's classes, and can run important functions, with a lot of waiting, asynchronously. These functions could for instance be WinSock operations, disk read/writes or similar. Functions that just need to wait for a while and then run some code would be better off being implemented by using the SetThink functions.

Threads in Source

Threads in Source are child classes of CWorkerThread. These contain a number of variables, a calling command, and the mainloop int Run(). An instance of the class is declared inside a source file. The other threads can call this thread by calling the proper functions in this instance. The thread is not nessesarily running when these functions are executed and will most likely be required to be started prior to any calls. A sample thread with a single function would look like this.

	//-----------------------------------------------------------------------------
	// A thread for async PingBack'ing.
	//-----------------------------------------------------------------------------
	class CMyAsyncThread: public CWorkerThread 
	{
	public:
		  CAsyncPingBackThread() :
		  m_Parameter1( NULL ) ,
		  m_Parameter2( NULL )
		  {
			  SetName( "MyAsyncThread" );
		  }

		  ~CAsyncPingBackThread()
		  {
		  }

		  enum
		  {
			  CALL_FUNC,
			  EXIT,
		  };

		  bool	CallThreadFunction( char* Parameter1, char* Parameter2 )
		  {
			  Assert( !Parameter1);
			  Assert( !Parameter2 );
			  m_Function = Function;
			  m_Parameter = Parameter;
			  CallWorker( CALL_FUNC );
			  Assert( !Parameter1);
			  Assert( !Parameter2 );
			  return true;
		  }

		  int Run()
		  {
			  unsigned nCall;
			  while ( WaitForCall( &nCall ) )
			  {
				  if ( nCall == EXIT )
				  {
					  Reply( 1 );
					  break;
				  }

				  // Reset some variables
				  char* Parameter1 = m_Parameter2;
				  char* Parameter2 = m_Parameter1;				
				  m_Parameter1 = 0;
				  m_Parameter2 = 0;

				  Reply( 1 );
				  FunctionToBeRunFromInsideTheThread(Parameter1,Parameter2);

			  }
			  return 0;
		  }

	private:
		char* m_Parameter1;
		char* m_Parameter2;
	};

	static CMyAsyncThread g_CMyAsyncThread;
	// Connects to the content servers and executes a function there with a given parameter
	// Runs the code in this thread, so the game will hang until done. Avoid this by using PingBack() instead.
	bool	PingBackThreaded( char* Function, char* Parameter )
	{

	}

	//-----------------------------------------------------------------------------
	// A thread for async PingBack'ing.
	//-----------------------------------------------------------------------------
	class CAsyncPingBackThread : public CWorkerThread 
	{
	public:
		  CAsyncPingBackThread() :
		  m_Function( NULL ) ,
		  m_Parameter( NULL )
		  {
			  SetName( "AsyncPingBackThread" );
		  }

		  ~CAsyncPingBackThread()
		  {
		  }

		  enum
		  {
			  CALL_FUNC,
			  EXIT,
		  };

		  bool	PingBack( char* Function, char* Parameter )
		  {
			  Assert( !Function );
			  Assert( !Parameter );
			  m_Function = Function;
			  m_Parameter = Parameter;
			  CallWorker( CALL_FUNC );
			  Assert( !Function );
			  Assert( !Parameter );
			  return true;
		  }

		  int Run()
		  {
			  unsigned nCall;
			  while ( WaitForCall( &nCall ) )
			  {
				  if ( nCall == EXIT )
				  {
					  Reply( 1 );
					  break;
				  }

				  if (!m_Function)
				  {
					  IngameError("MaxsiDistribution::CAsyncPingBackThread::Run() does not have m_Function set!");
					  m_Function = 0;
					  m_Parameter = 0;			
					  Reply( 0 );
					  return 0;
				  }

				  if (!m_Parameter)
				  {
					  IngameError("MaxsiDistribution::CAsyncPingBackThread::Run() does not have m_Parameter set!");
					  m_Function = 0;
					  m_Parameter = 0;					 
					  Reply( 0 );
					  return 0;
				  }

				  // Reset some variables
				  char* Function = m_Function;
				  char* Parameter = m_Parameter;				
				  m_Function = 0;
				  m_Parameter = 0;

				  Reply( 1 );
				  MaxsiDistribution::PingBackThreaded(Function,Parameter);

				  // Clean up
				  delete[] Function;
				  delete[] Parameter;
			  }
			  return 0;
		  }

	private:
		char* m_Function;
		char* m_Parameter;
	};

	static CAsyncPingBackThread g_AsyncPingBackThread;


	// Creates the thread, from there calls the pingback and then does fancy stuff.
	bool	PingBack( char* Function, char* Parameter )
	{		
		if ( !g_AsyncPingBackThread.IsAlive() )
		{
			g_AsyncPingBackThread.Start();
		}
		if ( !g_AsyncPingBackThread.IsAlive() )
		{
			Warning(":PingBack() failed to start the async pingback thread!");
		}

		// Make some local copies! (Because the real ones COULD get deleted or changed while we upload)
		char* NewFunction	=	BuildString(1,Function);
		char* NewParameter	=	BuildString(1,Parameter);
		
		g_AsyncPingBackThread.PingBack ( NewFunction, NewParameter);

		g_AsyncPingBackThread.CallWorker( CAsyncPingBackThread::EXIT );
		return true;
	}