• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 2147
  • Last Modified:

Exiting CWinThread

Hi,
 
I am playing aroung with a multi threaded app and sockets and have the following situation...
 
I have a AsyncSocket in which I override OnAccept.
{
CConnectionThread *pConnection = new CConnectionThread();
 
 pConnection->CreateThread();
 if (!Accept(pConnection->m_cSocket))
 {
   // Exit the thread
 }
 else
 {
  CString strMessage = "Welcome...\n";
  pConnection->m_cSocket.Send(strMessage,strlen(strMessage));
  delete(pConnection);
  CloseHandle(pConnection->m_hThread);
 }

where CConnectionThread is derived from CWinThread and m_cSocket is a CAsyncSocket member variable in CConnectionThread
 
The above works fine. All incoming connection request spawns this CConnectionThread and I receive the Welcome message
 
My problem is now that I can't seem to get these threads to exit in any way. I have tried calling ExitThread and _endthread, but they close the "listening thread" instead of the newly created client thread. I have tried delete(pConnection), but although it destroys the class, the thread still remains active. I have tried CloseHandle and other less applicaple options, nothing seems to work. The thread never closes.
 
Does anybody have an idea of how I can close (kill) the connection thread?
 
Thanx
0
OD
Asked:
OD
  • 2
  • 2
1 Solution
 
ZoppoCommented:
Hi OD,

This is from MSDN:

--------------------------------------------------------------------------------------------------------------
Normal Thread Termination
For a worker thread, normal thread termination is simple: Exit the controlling function and return a value that signifies the reason for termination. You can use either theAfxEndThread function or a return statement. Typically, 0 signifies successful completion, but that is up to you.

For a user-interface thread, the process is just as simple: from within the user-interface thread, call::PostQuitMessage in the Win32 Programmer’s Reference, Volume 4. The only parameter that ::PostQuitMessage takes is the exit code of the thread. As for worker threads, 0 typically signifies successful completion.
--------------------------------------------------------------------------------------------------------------

Because your using a CWinThread derived class you'll have to use the second method (for user-interface thread).

Now, I think best for this will be to send the thread a user defined message using CWinThread::PostThreadMessage() whose messagehandler then can call the ::PostQuitMessage() inside the thread.

hope that helps,

ZOPPO
0
 
stefanrCommented:
The nice way of terminating the thread is, as mentioned before, to let the thread terminate itself. That would of course only work if the thread is not making a blocking call.

BTW, the preferred way of starting an MFC thread is through AfxBeginThread. That way, the thread object would (by default) be automatically deleted when the thread exits.

If the thread is an UI thread (a thread that dispatches messages, that is, if InitInstance returns TRUE) you could terminate the thread by posting it a WM_QUIT message, i.e. pConnection->PostThreadMessage(WM_QUIT, 0, 0)). (It is BTW this method that is used in the ATL Wizard when creating a Service.) If the thread has a main window, you could post that window a WM_CLOSE message. In fact, the intended way of communicating with an UI thread is through messages.

Another method could be to use an event object accessable by both the main thread and the spawned thread. Then you could wait a short time for the event to be set in the thread, and if it is, exit it (using for example AfxEndThread). An example:

static CEvent g_eventExitThread(FALSE, FALSE); // Global automatic reset event; a manual reset event would have (FALSE, TRUE).

   . . .

void CMySocket::OnAccept(int nErrorCode)
{
   if (0 == nErrorCode)
   {
      . . .
      g_eventExitThread.SetEvent();
   }
}
   . . .

// Code inside thread function below.

CSingleLock lockExitThread(&g_eventExitThread);

while (TRUE)
{
   if (lockExitThread.Lock(0))
   {
      ::AfxEndThread(0); // Or return 0;
   }

   . . .
}

A thing that complicates the creation of a thread is that it may take a while to start up. That would mean that the thread may have not started up yet when you exit the OnAccept function. If it is important to be sure that the thread is up and running, you could use another event object that could signal the main thread that the spawned thread has started.

There is also an ugly way of terminating a thread, that is not recommended unless in extreme cases. Call the WIN32 function BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode) to forcefully terminate any thread from another. Beware though, since this brute force can make the application (and even the system) unstable. On NT/2000 the application also requires the THREAD_TERMINATE access. The thread handle in your case is pConnection->m_hTread.
0
 
ODAuthor Commented:
If I terminate my CMyWinThread with
      ::AfxEndThread(0) (this code is definitately within the CWinThread class)
it terminates my application thread as well and my application (and all threads) terminate.

Why does that happen?

I also get an Assertion error on my Socket (member variable of the CMyWinThread class) when the thread terminates, any ideas?

I believe that you probably have answered my question, but if you can answer these additional ones I'll increase the points.

0
 
stefanrCommented:
It looks like that ::AfxEndThread(0) is called from the primary thread, even if it is called from within the CWinThread class. That could happen if a member function that, directly or indirectly, calls ::AfxEndThread is called from the primary thread. You can verify which thread calls that function by using ::GetCurrentThreadId just before ::AfxEndThread and compare its return value with ::AfxGetApp()->m_nThreadId and pConnection->m_nThreadId.
The assert can occur when certain socket member functions is called from a thread other to which it is mapped (normally the thread that created the socket). In that case, it would be an ASSERT_VALID(this) that asserts. It can also occur when an outstanding socket event causes the callback function (OnReceive, et al) to be called when the socket object is already deleted/destroyed. It is quite tricky to avoid that situation in a multithreaded environment. One simple idea may be to call AsyncSelect(0), then sleep a while before calling delete.
0
 
ODAuthor Commented:
I eventually used the CSingleLock method described in the answer in InitInstance of the CMyWinThread. If the lock is attained false is returned and the thread exits.

The assertion error came from m_cSocket.Close in ExitInstance of this CMyWinThread class. I moved the code to OnClose of my socket, thus the code in CMySocket is:
void CMySocket::OnClose(int nErrorCode)
{
      Close();
.....

That immediately sorted out the Assertion error, although I do not understand why it worked.
0

Featured Post

Free Tool: Subnet Calculator

The subnet calculator helps you design networks by taking an IP address and network mask and returning information such as network, broadcast address, and host range.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

  • 2
  • 2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now