C++ und Threads

11/12/2010 - 13:52 von Mario | Report spam
Hallo,

ich setze mich momentan mit Threads in C++ auseinander und habe da ein
Problem, wo
ich gerade nicht so recht weiterkomme. Bin noch ziemlich unerfahren auf
diesem Gebiet.

Ich verwende die Windows-API und Viusal C++ 2008.
Das ganze ist ein Commando-Zeilen-Programm.

Folgendes möchte ich lösen:

Es sollen z.B. x Aufgaben abgearbeitet werden. Jede Aufgabe wird in einem
Thread ausgeführt.

Maximal sollen aber immer 4 Threads gleichzeitig laufen und sobald eine
Aufgabe beendet ist und somit auch der Thread,
soll der nàchste Thread automatisch gestartet werden usw. bis alle x
Aufgaben abgearbeitet sind. Und es sollen nach Möglichkeit max. immer 4
Threads laufen.

Mit WaitForMultipleObjects habe ich es hinbekommen, dass sobald alle 4
Aufgaben erledigt sind, die nàchsten 4 Aufgaben gestartet werden.
Da die Aufgaben aber unterschiedlich lange laufen, wàre diese Lösung nicht
sehr effizient.

Aber mit WaitForSingleObject bekomme ich es nicht so richtig hin.

Irgendwie stehe ich gerade auf dem Schlauch...

Vielleicht kann mir jemand auf die Sprünge helfen.

Danke.

Gruß

Mario
 

Lesen sie die antworten

#1 Volker Bartheld
11/12/2010 - 16:21 | Warnen spam
Hallo!

On Sat, 11 Dec 2010 13:52:27 +0100, Mario wrote:
Es sollen z.B. x Aufgaben abgearbeitet werden. Jede Aufgabe wird in einem
Thread ausgeführt. Maximal sollen aber immer 4 Threads gleichzeitig
laufen und sobald eine Aufgabe beendet ist und somit auch der Thread,
soll der nàchste Thread automatisch gestartet werden

Mit WaitForMultipleObjects habe ich es hinbekommen, dass sobald alle 4
Aufgaben erledigt sind, die nàchsten 4 Aufgaben gestartet werden.



Klar. Da wartest Du auf alle 4 Handles gleichzeitig.

Aber mit WaitForSingleObject bekomme ich es nicht so richtig hin.



Du kannst im Main Thread WaitForSingleObject() mit einem ertràglichen
Timeout benutzen, "pollst" also quasi die Threads nacheinander, ob sie noch
laufen. Das könnte freilich auch in einem "Controller-Thread" passieren,
damit der Main Thread frei für andere Aufgaben ist. Wenn ein Thread
terminiert, erzeugst Du einen neuen, mit einer neuen Aufgabe. Das erzeugt
einen gewissen - manchmal aber durchaus vernachlàssigbaren Overhead.

Alternativ schaust Du Dir mal ITC (Inter-Thread-Communication) an. Da
könnte ein (Worker)Thread dem (Main)Thread Bescheid sagen (z. B. via
::PostThreadMessage()), daß er mit der Arbeit fertig ist und der Main
Thread erteilt ihm dann einen neuen Auftrag. WaitForSingleObject() erübrigt
sich dann und Du kannst einfach entsprechende Messages hin- und
herschicken.

Das bedeutet aber, daß Du in Deinem Main Thread eine Messageloop einrichten
mußt, die ist in einer simplen Kommandozeilenapp nicht von Haus aus dabei.
M. W. n. kannst Du das aber auch dort mit einem while( GetMessage( &msg,
NULL, 0, 0 ) > 0 )-Konstrukt nachholen.

Ich habe im Anhang mal was beigepackt, evtl. hilft Dir das weiter. Du
würdest dann von CThread ableiten, die entsprechenden Memberfunktion
implementieren/überschreiben - i. Allg. wàre das

virtual void Run();
virtual void OnThreadMessage( MSG* pMsg ) {}
virtual void OnThreadStart() {}
virtual void OnThreadFinish() {}

und dann eine Instanz von, sagen wir, CMyThread MyThread erstellen.

Da ruftst Du dann MyThread.StartThread() auf, um ihn zu starten on fütterst
ihn über MyThread.PostMessage() mit Befehlen. Wenn Du Dir im
Cthread-Konstruktor die Thread-ID des Main Threads (oder einen
Windows-Handle) merkst, dann kannst Du mit ::PostMessage() oder
::PostThreadMessage() auch aus dem Worker Thread in den Main Thread
kommunizieren.

Ich hoffe, behilflich gewesen zu sein.

Ciao,
Volker


<syncobj.cpp>
/////////////////////////////////////////////////////////////////////////////////////////
// class CMutex

CMutex::CMutex()
{
InitializeCriticalSection( &_crit_section );
}

CMutex::~CMutex()
{
DeleteCriticalSection( &_crit_section );
}

void CMutex::Seize()
{
EnterCriticalSection( &_crit_section );
}

void CMutex::Release()
{
LeaveCriticalSection( &_crit_section );
}

/////////////////////////////////////////////////////////////////////////////////////////
// class CLock

CLock::CLock( CMutex& mutex_ )
: _mutex(mutex_)
{
_mutex.Seize();
}

CLock::~CLock()
{
_mutex.Release();
}

/////////////////////////////////////////////////////////////////////////////////////////
// class CInterlockedLong

CInterlockedLong::CInterlockedLong( LONG _value )
: value(_value)
{
}

LONG CInterlockedLong::Increment()
{
return ::InterlockedIncrement( &value );
}

LONG CInterlockedLong::Decrement()
{
return ::InterlockedDecrement( &value );
}

/////////////////////////////////////////////////////////////////////////////////////////
// class CEvent

CEvent::CEvent( BOOL bManualReset, BOOL bInitialState )
{
_handle = CreateEvent( 0, bManualReset, bInitialState, 0 );
}

CEvent::~CEvent()
{
CloseHandle( _handle );
}

void CEvent::Signal()
{
::SetEvent( _handle );
}

void CEvent::Reset()
{
::ResetEvent( _handle );
}

BOOL CEvent::Wait( DWORD dwMilliseconds ) const
{
return ::WaitForSingleObject( _handle, dwMilliseconds ) == WAIT_OBJECT_0;
}

BOOL CEvent::IsSignaled() const
{
if( ::WaitForSingleObject( _handle, 0 ) == WAIT_OBJECT_0 )
return TRUE;

return FALSE;
}

/////////////////////////////////////////////////////////////////////////////////////////
// class CThread

CThread::CThread()
: m_threadHandle(NULL), m_threadID(0)
{
}

CThread::~CThread()
{
ASSERT( m_threadHandle == NULL );
}

void CThread::SetPriority( int nPriority )
{
::SetThreadPriority( m_threadHandle, nPriority );
}

void CThread::StartThread()
{
ASSERT( m_threadHandle == NULL );

m_threadHandle =
(HANDLE)_beginthreadex( NULL, 0, ThreadFunction, this, 0, &m_threadID ); // start a worker thread and pass in a this-pointer which will be reinterpret_cast-ed (see also http://www.mpdvc.de/html.htm#Q18)

m_eventInit.Wait(); // wait until initialization is complete
}

BOOL CThread::StopThread( BOOL bSendQuitMessage, BOOL bKillNotResponce )
{
if( m_threadHandle == NULL ) return TRUE;

if( bSendQuitMessage )
PostMessage( WM_QUIT, 0, 0 );

BOOL bSuccess = m_eventStopped.Wait( 5000 ); // gracefully wait 5s for thread to signal that it has terminated before killing it if necessary

if( !bSuccess && bKillNotResponce ) // thread termination not signaled: kill thread if requested
{
TRACE( "Terminating a not-responding thread: handle 0x%X, id %d.", m_threadHandle, m_threadID );
TerminateThread( m_threadHandle, 0xDEAD );
}

CloseHandle( m_threadHandle );

m_threadHandle = NULL;
m_threadID = 0;

return bSuccess;
}

BOOL CThread::PostMessage( UINT Msg, WPARAM wParam, LPARAM lParam ) // send a message to worker thread
{
if( m_threadID == 0 ) return FALSE; // thread not active, skip posting the message
return ::PostThreadMessage( m_threadID, Msg, wParam, lParam ); // use the Win API function to send a message to the CThread-object
}

void CThread::Run() // worker thread main message loop
{
OnThreadStart(); // execute some starting code in overloaded class if necessary

MSG msg;
while( GetMessage( &msg, NULL, 0, 0 ) > 0 ) // run message-loop unless WM_QUIT has been received
{
OnThreadMessage( &msg ); // process message
}

if( msg.message == WM_QUIT ) OnThreadMessage( &msg );

OnThreadFinish(); // do some cleanup stuff in overloaded class if necessary
}

UINT __stdcall CThread::ThreadFunction( void* pParam ) // this is the static callback function for _beginthreadex()
{
CThread& self = *reinterpret_cast<CThread*>( pParam ); // extract "this" pointer

MSG msg;
PeekMessage( &msg, NULL, WM_USER, WM_USER, PM_NOREMOVE ); // peek into the message buffer of the current thread for WM_USER messages but don't remove the message from there (to create msg loop)

self.m_eventInit.Signal(); // signal the initialization has completed (blocked function CThread::StartThread() in main thread context will be continued)

self.Run(); // run the thread

self.m_eventStopped.Signal();

return 1;
}
</syncobj.cpp>

<syncobj.h>
/////////////////////////////////////////////////////////////////////////////////////////
// CMutex

class CMutex
{
private:
CRITICAL_SECTION _crit_section;

public:
CMutex();
~CMutex();

void Seize();
void Release();
};

class CLock
{
private:
CMutex& _mutex;

public:
CLock( CMutex& mutex_ );
~CLock();
};

/////////////////////////////////////////////////////////////////////////////////////////
// CInterlockedLong

class CInterlockedLong
{
private:
LONG value;

public:
CInterlockedLong( LONG _value );

// under Windows 95 only sign of the returning value is correct

LONG Increment();
LONG Decrement();
};

/////////////////////////////////////////////////////////////////////////////////////////
// CEvent

class CEvent
{
private:
HANDLE _handle;

public:
CEvent( BOOL bManualReset = FALSE, BOOL bInitialState = FALSE );
~CEvent();

operator HANDLE()
{
return _handle;
}

void Signal();
void Reset();

BOOL Wait( DWORD dwMilliseconds = INFINITE ) const;
BOOL IsSignaled() const;
};

/////////////////////////////////////////////////////////////////////////////////////////
// CThread

class CThread // generic worker thread class
{
public:

HANDLE m_threadHandle;
UINT m_threadID;

CThread();
~CThread();

void StartThread();
BOOL StopThread( BOOL bSendQuitMessage = TRUE, BOOL bKillNotResponce = FALSE );

BOOL IsThreadRunning() { return m_threadHandle != NULL; }

void SetPriority( int nPriority );
BOOL PostMessage( UINT Msg, WPARAM wParam, LPARAM lParam );

protected:

virtual void Run();
virtual void OnThreadMessage( MSG* pMsg ) {}
virtual void OnThreadStart() {}
virtual void OnThreadFinish() {}

private:
static UINT __stdcall ThreadFunction( void* pParam );
CEvent m_eventInit, m_eventStopped;
};
</syncobj.h>




@: I N F O at B A R T H E L D dot N E T
3W: www.bartheld.net

Ähnliche fragen