[WINAPI] WaitForMultipleObjects wartet nicht

FBIagent

Erfahrenes Mitglied
Nabend,

ich weis zu diesem Problem gibt es schon etliche Themen, nur hat mir keins davon
wirklich weitergeholfen. Ich habe mit auch die Beschreibung hierzu in der MSDN etliche
Male angeschaut nur sehe ich dort keinen unterschied zu dem was ich mache. Das
einzigste was mir aufffiel ist, dass die HANDLES das SYNCHRONIZE security Attribute
brauchen. Da dies allerdings standardmäßig schon gesetzt wird wenn ich einen Thread
erstelle, habe ich mir darüber nicht die meisten Gedanken gemacht.

C++:
#ifndef __IOCP_H__
#define __IOCP_H__

#include <windows.h>

class IOCP {
public:
	IOCP( DWORD NumWorkers );
	virtual ~IOCP();

	DWORD Worker();
	bool Associate( HANDLE FileHandle, ULONG_PTR CompletionKey, DWORD NumConcurrentThreads );
	void NotifyWorkersToTerminate();
	void WaitForWorkersToTerminate();

	bool IsOk();
	DWORD GetNumWorkers();
private:
	HANDLE m_IOCPHandle;
	DWORD m_NumWorkers;
	HANDLE *m_WorkerThreadHandles;
	DWORD *m_WorkerThreadIds;
	bool m_NotifiedStop;

	static DWORD WINAPI IOCP_WorkerThread( LPVOID lpvData ) {
		return ( ( IOCP* )lpvData )->Worker();
	}

	virtual void WorkerStart() = NULL;
	virtual void WorkerHandleGone( ULONG_PTR CompletionKey, LPOVERLAPPED Overlapped ) = NULL;
	virtual void WorkerCompletion( DWORD BytesTransfered, ULONG_PTR CompletionKey, LPOVERLAPPED Overlapped ) = NULL;
	virtual void WorkerFinish() = NULL;
};

#endif


#include "IOCP.h"

IOCP::IOCP( DWORD NumWorkers ) 
: m_IOCPHandle( NULL ), m_NumWorkers( NumWorkers ), m_WorkerThreadHandles( NULL ), m_WorkerThreadIds( NULL ), m_NotifiedStop( false ) {
	if ( ( m_IOCPHandle = CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, 0, 0) ) == NULL ) {
		return;
	}

	if ( ( m_WorkerThreadHandles = new HANDLE[ m_NumWorkers ] ) == NULL ) {
		CloseHandle( m_IOCPHandle );
		m_IOCPHandle = NULL;
		return;
	}

	if ( ( m_WorkerThreadIds = new DWORD[ m_NumWorkers ] ) == NULL ) {
		delete[] m_WorkerThreadHandles;
		m_WorkerThreadHandles = NULL;
		CloseHandle( m_IOCPHandle );
		m_IOCPHandle = NULL;
		return;
	}

	for ( DWORD dw = 0;dw < m_NumWorkers;++ dw ) {
		if ( ( m_WorkerThreadHandles[ dw ] = CreateThread( NULL, 0, IOCP_WorkerThread, this, 0, &m_WorkerThreadIds[ dw ] ) ) == NULL ) {
			for ( DWORD dw2 = 0;dw2 < dw;++ dw2 ) {
				PostQueuedCompletionStatus( m_IOCPHandle, 0, 0, NULL );
			}

			WaitForMultipleObjects( dw, m_WorkerThreadHandles, TRUE, INFINITE );
			delete[] m_WorkerThreadHandles;
			delete[] m_WorkerThreadIds;
			m_WorkerThreadHandles = NULL;
			m_WorkerThreadIds = NULL;
			CloseHandle( m_IOCPHandle );
			m_IOCPHandle = NULL;
			return;
		}
	}
}

IOCP::~IOCP() {
	NotifyWorkersToTerminate();
	WaitForWorkersToTerminate();

	if ( m_WorkerThreadHandles != NULL ) {
		delete[] m_WorkerThreadHandles;
	}

	if ( m_WorkerThreadIds != NULL ) {
		delete[] m_WorkerThreadIds;
	}

	CloseHandle(m_IOCPHandle);
	m_IOCPHandle = NULL;
}

DWORD IOCP::Worker()
{
	WorkerStart();

	DWORD BytesTransfered = 0;
	ULONG_PTR CompletionKey = 0;
	LPOVERLAPPED Overlapped = NULL;
	BOOL CompletionStatus = FALSE;

	while ( true ) {
		CompletionStatus = GetQueuedCompletionStatus( m_IOCPHandle, &BytesTransfered, &CompletionKey, &Overlapped, INFINITE);

		/* Notify Stop ? */
		if ( BytesTransfered == 0 && CompletionKey == 0 && Overlapped == NULL ) {
			break;
		}

		if ( CompletionStatus == FALSE || ( CompletionStatus == TRUE && BytesTransfered == 0 ) ) {
			WorkerHandleGone( CompletionKey, Overlapped );
			continue;
		}

		WorkerCompletion( BytesTransfered, CompletionKey, Overlapped );
	}

	WorkerFinish();
	return 0;
}

bool IOCP::Associate( HANDLE FileHandle, ULONG_PTR CompletionKey, DWORD NumConcurrentThreads ) {
	return CreateIoCompletionPort( FileHandle, m_IOCPHandle, CompletionKey, NumConcurrentThreads ) != NULL;
}

void IOCP::NotifyWorkersToTerminate() {
	m_NotifiedStop = true;

	for ( DWORD dw = 0;dw < m_NumWorkers;++ dw ) {
		PostQueuedCompletionStatus( m_IOCPHandle, 0, 0, NULL );
	}
}

void IOCP::WaitForWorkersToTerminate() {
	WaitForMultipleObjects( m_NumWorkers, m_WorkerThreadHandles, TRUE, INFINITE );
}

bool IOCP::IsOk() {
	return m_IOCPHandle != NULL;
}

DWORD IOCP::GetNumWorkers() {
	return m_NumWorkers;
}


#ifndef __MYIOCP_H__
#define __MYIOCP_H__

#include "IOCP.h"
#include "SyncPrintf.h"

#include <windows.h>

class MyIOCP
: public IOCP {
public:
	virtual ~MyIOCP() {
	}

	static MyIOCP *GetSingleton() {
		static MyIOCP Singleton;
		return &Singleton;
	}
private:
	MyIOCP()
	: IOCP( 2 ) {
	}

	void WorkerStart() {
		SyncPrintf( "Worker Started: %d\n", GetCurrentThreadId() );
	}

	void WorkerHandleGone( ULONG_PTR CompletionKey, LPOVERLAPPED Overlapped ) {
	}

	void WorkerCompletion( DWORD BytesTransfered, ULONG_PTR CompletionKey, LPOVERLAPPED Overlapped ) {
	}

	void WorkerFinish() {
		SyncPrintf( "Worker Finished: %d\n", GetCurrentThreadId() );
	}
};

#endif

void TerminateIOCP() {
    MyIOCP::GetSingleton()->NotifyWorkersToTerminate();
    MyIOCP::GetSingleton()->WaitForWorkersToTerminate();
}

int main()
{
    if ( !MyIOCP::GetSingleton()->IsOk() ) {
        std::cout << "Couldn't initialize IOCP" << std::endl;
        return 1;
    }

    while ( !kbhit() ) {
        Sleep(1);
    }

    TerminateIOCP();
}

Ich bin darauf gekommen das es an WaitForMultipleObjects liegt, weil ich am ende des
Programms einen MSVC++ Runtime Error bekomme, der mir sagt, dass das programm
sich noch in einer Methode von static MyIOCP Singleton; befindet, aber gleichzeitig
auch das Objekt zerstören zu versucht. Es ist wohl eher Zufall aber der Debugger sagt mir
immer das das Programm noch in einem pure virtual call ist.

Nun wäre ich für einen Rat sehr dankbar.

Best wishes
FBIagent
 
Zuletzt bearbeitet von einem Moderator:
Ich komme leider nicht auf den Fehler... wäre schön wenn sich das nocheinmal jemand anschaut :)

Best wishes
FBIagent
 
Nun denn, ich habe die Ursache für das Problem gefunden, den Grund allerdings nicht.
Wenn ich die Threads im Konstruktor erstelle, wird nicht gewartet. Nun habe ich mir eine
neue Methode implementiert die ich aufufen muss, damit die Threads gestartet werden.

Könnte mir jemand erklären warum die Threadhandles für WaitForXXX nicht gültig sind wenn sie in einem Konstruktor erstellt wurden?

Best wishes
FBIagent
 

Neue Beiträge

Zurück