Threads in Windows service
-
I have a Windows service in which I need to stop worker threads from my main thread. I am trying to use events to do this but I am stumped on why it is not working. Each class that runs a worker thread creates a stop event and a response event. When I need to stop the worker thread I call into the threads class and set the event. However, instead of the worker thread detecting that the event has been set and stopping gracefully, it just disappears. Why? What is the best method for stopping a worker thread from the main thread? Re-posted from the C# forum.
-
I have a Windows service in which I need to stop worker threads from my main thread. I am trying to use events to do this but I am stumped on why it is not working. Each class that runs a worker thread creates a stop event and a response event. When I need to stop the worker thread I call into the threads class and set the event. However, instead of the worker thread detecting that the event has been set and stopping gracefully, it just disappears. Why? What is the best method for stopping a worker thread from the main thread? Re-posted from the C# forum.
This should work quite well. I do it all the time. Something else is wrong. Of course, your thread must periodically be 'waiting'on the event. If you require a polled approach because your thread is always working and does not return to check for the 'main event' too often, then you can use InterLockedIncrement and InterlockedCompareExchange on a control variable.
-
This should work quite well. I do it all the time. Something else is wrong. Of course, your thread must periodically be 'waiting'on the event. If you require a polled approach because your thread is always working and does not return to check for the 'main event' too often, then you can use InterLockedIncrement and InterlockedCompareExchange on a control variable.
I appreciate any help. I have posted my code below:
void CFileChangeEvent::stopProcessThread() { TRACE( "> > > > CFileChangeEvent::stopProcessThread() entered - Event handles < %X, %X >, Event ID < %s >\n", m_hProcessStopEvent, m_hProcessRespEvent, getIdentifier() ); // The ProcessStopEvent is used by the main thread to tell the worker thread to stop. // In turn, the worker thread sets the ProcessRespEvent to tell the main thread that it has stopped. ::SetEvent( m_hProcessStopEvent ); } UINT CFileChangeEvent::MultiFileProcessThread( LPVOID lpParam ) { // This is the threaded multi-file processor. It searches for and processes all raw flight data // (*.FIF files) in the Inbox or until it is cancelled by a call from DataMovement due to // a Cancel message from PC-GBS. CFileChangeEvent* pEvent = (CFileChangeEvent*)lpParam; CFlightDataHandler* pFlightDataHandler = NULL; CLogWriter* pLogWriter = NULL; HANDLE hProcessStopEvent = pEvent->getProcessStopEvent(); HANDLE hProcessRespEvent = pEvent->getProcessRespEvent(); // We use this exception handling merely to protect this service // from being crashed by an unhandled exception. Errors are // generally handled and logged within the DLL by the LogWriter. try { CDataMovement* pDataMovement = pEvent->getDataMovementParent(); CLogWriter* pLogWriter = pDataMovement->getLogWriter(); pFlightDataHandler = new CFlightDataHandler( pLogWriter ); // We must call ::CoInitialize() each time the database is accessed HRESULT hRes = ::CoInitialize(NULL); CString strInboxPath( pDataMovement->getInboxPath() ); vector<CString> vecFifFiles = UtilK::findAllFiles( "*.fif", strInboxPath ); // The input file parameter points to a single *.fif file, but we // need to extract the folder just below the Inbox path so that // all flight data in that path can be processed int nLen = strInboxPath.GetLength() + 1; vector<CString>::iterator iterFile = vecFifFiles.begin(); CString strFile = *iterFile; CString strLastPath = (*iterFile).Mid( 0, strFile.Find( "\\", nLen ) ); int nFileCount = 0; bool bCancelled = false; for( ; iterFile != vecFifFiles.end() && ! bCancelled; iterFile++ ) { CString strPath = UtilK::getFsPart( *iterFile, UtilK::FsPath ); pFlightDataHandler->importRawFlightData( strPath ); DWORD dwWaitStatus = ::WaitForSingleObject( hProcessStopEvent, 1 ); if ( dwWaitStatus == WAIT_FAILED
-
I appreciate any help. I have posted my code below:
void CFileChangeEvent::stopProcessThread() { TRACE( "> > > > CFileChangeEvent::stopProcessThread() entered - Event handles < %X, %X >, Event ID < %s >\n", m_hProcessStopEvent, m_hProcessRespEvent, getIdentifier() ); // The ProcessStopEvent is used by the main thread to tell the worker thread to stop. // In turn, the worker thread sets the ProcessRespEvent to tell the main thread that it has stopped. ::SetEvent( m_hProcessStopEvent ); } UINT CFileChangeEvent::MultiFileProcessThread( LPVOID lpParam ) { // This is the threaded multi-file processor. It searches for and processes all raw flight data // (*.FIF files) in the Inbox or until it is cancelled by a call from DataMovement due to // a Cancel message from PC-GBS. CFileChangeEvent* pEvent = (CFileChangeEvent*)lpParam; CFlightDataHandler* pFlightDataHandler = NULL; CLogWriter* pLogWriter = NULL; HANDLE hProcessStopEvent = pEvent->getProcessStopEvent(); HANDLE hProcessRespEvent = pEvent->getProcessRespEvent(); // We use this exception handling merely to protect this service // from being crashed by an unhandled exception. Errors are // generally handled and logged within the DLL by the LogWriter. try { CDataMovement* pDataMovement = pEvent->getDataMovementParent(); CLogWriter* pLogWriter = pDataMovement->getLogWriter(); pFlightDataHandler = new CFlightDataHandler( pLogWriter ); // We must call ::CoInitialize() each time the database is accessed HRESULT hRes = ::CoInitialize(NULL); CString strInboxPath( pDataMovement->getInboxPath() ); vector<CString> vecFifFiles = UtilK::findAllFiles( "*.fif", strInboxPath ); // The input file parameter points to a single *.fif file, but we // need to extract the folder just below the Inbox path so that // all flight data in that path can be processed int nLen = strInboxPath.GetLength() + 1; vector<CString>::iterator iterFile = vecFifFiles.begin(); CString strFile = *iterFile; CString strLastPath = (*iterFile).Mid( 0, strFile.Find( "\\", nLen ) ); int nFileCount = 0; bool bCancelled = false; for( ; iterFile != vecFifFiles.end() && ! bCancelled; iterFile++ ) { CString strPath = UtilK::getFsPart( *iterFile, UtilK::FsPath ); pFlightDataHandler->importRawFlightData( strPath ); DWORD dwWaitStatus = ::WaitForSingleObject( hProcessStopEvent, 1 ); if ( dwWaitStatus == WAIT_FAILED
A couple things I see going on here. 1. You should verify pFlightDataHandler got created before using it. 2. I am assuming that the calls to getProcessStopEvent and getProcessRespEvent actually open a handle, otherwise, when you close them at the end of your thread, you have closed them off for the 'parent' as well. 3. It is not necessary to call AfxEndThread if you are going to exit the thread via its return. Just return the value 0 at end of thread function to accomplish same result. 4. I am assuming that pLogWriter's writeEntry will not throw exceptions, otherwise, you could just flag a problem in your exception handling code and then write to log after leaving the exception. 5. I would consider moving the CoInitialize ahead of your main try block, since the CoUninitialize is already outside of it. 6. If the pFlightDataHandler is a 'risky' object, consider putting its delete in a try/catch block. 7. Why not wait for 0 milliseconds, as per MSDN The function returns if the interval elapses, even if the object's state is nonsignaled. If dwMilliseconds is zero, the function tests the object's state and returns immediately. Then I think it might not even suspend your thread if the system is not busy. Otherwise, I am not sure why you would have a problem, unless one of your obejcts is bad, as there are a couple other retrieval functions there where the pointers are not verified against NULL.