- 浏览: 211000 次
- 性别:
- 来自: 成都
-
文章分类
- 全部博客 (231)
- C++学习 (41)
- vc学习 (25)
- vc异常 (4)
- Flex (2)
- Ext (0)
- java学习笔记 (15)
- mysql学习笔记 (1)
- ibatis学习笔记 (7)
- struts1学习笔记 (1)
- hibernate学习笔记 (3)
- javascript学习笔记 (8)
- eclipse快捷键 (2)
- 英语学习 (1)
- cmd (1)
- eclipse 配置 (2)
- 其他 (4)
- JAVASE (34)
- firefox (1)
- linux (8)
- Apache (6)
- cms (22)
- jsuc (10)
- html (3)
- css (1)
- 作图 (2)
- vs2012 (2)
- 编码转换 (12)
- work_unicode (1)
- work_webbrowser (1)
- work_trade_quotes_login (1)
- c#学习 (38)
- wpf学习 (31)
- wpf快速学习 (1)
- VB学习 (1)
- NSIS (4)
- log4cpp (3)
- 学习web (1)
- 经验 (1)
- c_汇编 (1)
最新评论
VC退出线程的方法
标题很简单,但是要讨论的地方不少;
多线程管理,在程序开发中非常重要;虽然应用比较简单,但是要用到不出错,就很难;
这里讲一下线程的退出方法;
一:简单说一下在windows上的线程启动:
方法(1): WIN32程序通用方法;
//线程:
DWORD WINAPI thread_testexit( PVOID pParam )
{
while( g_bExtiThread )
{
Sleep(1000);
static int i = 0;
CString str;str.Format( L"%d",i++);
AfxGetApp()->GetMainWnd()->SetWindowText( str );
}
return 0;
}
启动:
HANDLE hThread = ::CreateThread( 0, 0, thread_testexit, 0, NULL, NULL );
方法(2):MFC的方法
UINT thread_testexit( PVOID pParam )
{
while( g_bExtiThread )
{
Sleep(1000);
static int i = 0;
CString str;str.Format( L"%d",i++);
AfxGetApp()->GetMainWnd()->SetWindowText( str );
}
return 0;
}
启动:
CWinThread * pWinThreadtestexit = AfxBeginThread( thread_testexit, 0 );
二:线程的退出:
(1):
我想大部分人为了图方便,会定义一个BOOL变量如: BOOL g_bExtiThread ; 当 if( g_bExtiThread == 0 )的时候跳出线程循环,结束线程;
既:g_bExtiThread = 0;
只做这一步,会隐藏一个问题, 如果线程执行的时间较长,如循环中Sleep(1000); 这样会导致,执行 g_bExtiThread = 0; 后立即执行后面的函数,而不会等待线程结束;
如果线程中的变量鱼g_bExtiThread = 0;后面的执行相关,就可能隐藏问题;
(2):
这里自然,有人会用一个简单的方法避免这个过程就是:
g_bExtiThread = 0;
Sleep(2000);
这样, 等待线程结束后,执行其他语句;
这样做,有两个问题:1.效率上比较低,因为即使1000毫秒结束了,可是,却要等待Sleep(2000);
2.如果上述修改为:
g_bExtiThread = 0;
Sleep(1001);
这样看着不错,但是,如果线程中,Sleep (1000),软后语句执行的时间,大于1毫秒,也就是说 线程循环一次的时间 大于1001毫秒,仍然可能导致 “(1)”中的问题;
或者,线程很多,当线程执行到某一段的时候, CPU的时间片分配给其他线程,这样,依然会导致 线程中的循环时间无法确定;
想通过Sleep的方法等待线程循环结束,只有将时间给的很长,但是这样太浪费时间和效率了。
(3):
比较通用的方法:
通过WaitForSingleObject获取线程状态,如果线程退出,执行后面的语句;
g_bExtiThread = 0;
WaitForSingleObject( pWinThreadtestexit->m_hThread, INFINITE );
示例:
BOOL g_bExtiThread = TRUE;
CWinThread * pWinThreadtestexit = NULL;
UINT thread_testexit( PVOID pParam )
{
while( g_bExtiThread )
{
Sleep(1000);
static int i = 0;
CString str;str.Format( L"%d",i++);
//AfxGetApp()->GetMainWnd()->SetWindowText( str );
}
return 0;
}
void Ctmfc1Dlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
pWinThreadtestexit = AfxBeginThread( thread_testexit, 0 );
}
void Ctmfc1Dlg::OnBnClickedButton2()
{
// TODO: 在此添加控件通知处理程序代码
g_bExtiThread = 0;
WaitForSingleObject( pWinThreadtestexit->m_hThread, INFINITE );
SetWindowText( L"线程已经停止" );
}
这里,可能有人已经注意到了, //AfxGetApp()->GetMainWnd()->SetWindowText( str ); 我屏蔽了这句话;
这个问题留给读者,这里不再赘述了。
(4):
BOOL TerminateThread(
HANDLE hThread, // handle to thread
DWORD dwExitCode // exit code
);
这里推荐一篇文章:
TerminateThread is a dangerous function that should only be used in the most extreme cases. You should call TerminateThread only if you know exactly what the target thread is doing, and you control all of the code that the target thread could possibly be running at the time of the termination. For example, TerminateThread can result in the following problems:
If the target thread owns a critical section, the critical section will not be released.(未释放互斥区,造成死锁)
If the target thread is allocating memory from the heap, the heap lock will not be released.(未释放堆分配锁,造成死锁)
If the target thread is executing certain kernel32 calls when it is terminated, the kernel32 state fo the thread’s process could be inconsistent.(在执行内核函数时退出,造成该线程所在进程状态不确定,程序可能崩溃)
If the target thread is manipulating the global state of a shared DLL, the state of the DLL could be destroyed, affecting other users of the DLL.(在使用DLL时退出,造成DLL被销毁,其他使用该DLL得程序可能出现问题!)
A thread cannot protect itself against TerminateThread, other than by controlling access to its handles. The thread handle returned by the CreateThread and CreateProcess functions has THREAD_TERMINATE access, so any caller holding one of these handles can terminate your thread.
听过无数次不要TerminateThread,只是工作中常用,貌似也没有什么问题。今天在高强度测试中发现了一个不可原谅的错误。参看下面的例子
DWORD __stdcall mythread(void* )
{
while( true )
{
char* p = new char[1024];
delete p;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE h = CreateThread(NULL, 0, mythread, NULL, 0, NULL);
Sleep(1000);
TerminateThread(h, 0);
h = NULL;
char* p = new char[1024]; //这里会死锁,过不去
delete []p;
return 0;
}
为什么死锁呢?new操作符用的是小块堆,整个进程在分配和回收内存时,都要用同一把锁。如果一个线程在占用该锁时被杀死(即临死前该线程在new或delete操作中),其他线程就无法再使用new或delete了,表现为hang住。
《核心编程》里明确提醒不要TerminateThread,但原因并不是血淋淋滴。今天发现的这个bug印证了此书的价值。
另注:许多临时的网络操作经常用TerminateThread,作为网络不通时的退出机制,以后要改改了。比如让该线程自生自灭,自行退出。
再推荐一篇文章:
CloseHandle(),TerminateThread(),ExitThread()的区别
线程的handle用处:
线程的handle是指向“线程的内核对象”的,而不是指向线程本身.每个内核对象只是内核分配的一个内存块,并且只能由内核访问。该内存块是一种数据结构,它的成员负责维护对象的各种信息(eg: 安全性描述,引用计数等)。
CloseHandle()
在CreateThread成功之后会返回一个hThread的handle,且内核对象的计数加1,CloseHandle之后,引用计数减1,当变为0时,系统删除内核对象。
但是这个handle并不能完全代表这个线程,它仅仅是线程的一个“标识”,系统和用户可以利用它对相应的线程进行必要的操纵。如果在线程成功创建后,不再需要用到这个句柄,就可以在创建成功后,线程退出前直接CloseHandle掉,但这并不会影响到线程的运行。
不执行CloseHandle() 带来的后果:
若在线程执行完之后,没有通过CloseHandle()将引用计数减1,在进程执行期间,将会造成内核对象的泄露,相当与句柄泄露,但不同于内存泄露, 这势必会对系统的效率带来一定程度上的负面影响。但是,请记住,当进程结束退出后,系统仍然会自动帮你清理这些资源。但是在这里不推荐这种做法,毕竟不是 一个良好的编程习惯!
( 应用程序运行时,有可能泄露内核对象,但是当进程终止运行时,系统能确保所有内容均被正确地清除。另外,这个情况是用于所有对象,资源和内存块,也就是说,当进程终止时,系统将保证不会留下任何对象。)
TerminateThread()
函数的声明如下:
BOOL TerminateThread( HANDLE hThread, DWORD dwExitCode);
作用:
在线程外终止一个线程,用于强制终止线程。
参数说明:
HANDLE htread:被终止的线程的句柄,为CWinThread指针。
DWORD dwExitCode:退出码。
返回值:
函数执行成功则返回非零值,执行失败返回0。调用getlasterror获得返回的值。
听过无数次不要TerminateThread,只是工作中常用,貌似也没有什么问题。今天在高强度测试中发现了一个不可原谅的错误。参看下面的例子
[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
DWORD __stdcall mythread(void*)
{
while( true )
{
char* p = new char[1024];
delete [] p;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE h = CreateThread(NULL, 0, mythread, NULL, 0, NULL);
Sleep(1000);
TerminateThread(h , 0);
h = NULL;
char* p = new char[1024]; // 这里会死锁, 过不去
delete [] p;
return 0;
}
[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
DWORD __stdcall mythread(void*)
{
while( true )
{
char* p = new char[1024];
delete [] p;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE h = CreateThread(NULL, 0, mythread, NULL, 0, NULL);
Sleep(1000);
TerminateThread(h , 0);
h = NULL;
char* p = new char[1024]; // 这里会死锁, 过不去
delete [] p;
return 0;
}
为什么死锁呢?new操作符用的是小块堆,整个进程在分配和回收内存时,都要用同一把锁。如果一个线程在占用该锁时被杀死(即临死前该线程在new或delete操作中),其他线程就无法再使用new或delete了,表现为hang住。
<核心编程>里明确提醒不要TerminateThread,但原因并不是血淋淋滴。今天发现的这个bug印证了此书的价值。
另注:许多临时的网络操作经常用TerminateThread,作为网络不通时的退出机制,以后要改改了。比如让该线程自生自灭,自行退出。
ExitThread是推荐使用的结束一个线程的方法,当调用该函数时,当前线程的栈被释放,然后线程终止,相对于TerminateThread函数来说,这样做能够更好地完成附加在该线程上的DLL的清除工作
终止线程两个函数:ExitThread() 和 TerminateThread()
若要终止线程的运行,可以使用下面四种的方法:
线程函数退出循环来返回 (最佳方法 )。
通过调用ExitThread 函数,线程将自行撤消(尽量不要使用这种方法 )。
同一个进程或另一个进程中的线程调用TerminateThread 函数(最好避免使用这种方法 )。
该线程的主进程终止运行(避免使用 )。
下面将介绍终止线程运行的方法,并且说明线程终止运行时会出现什么情况。
1.线程函数返回
始终都应该将线程设计成这样的形式,即当想要线程终止运行时,它们就能够返回。这是
确保所有线程资源被正确地清除的唯一办法。
如果线程能够返回,就可以确保下列事项的实现:
a) 在线程函数中创建的所有C + +对象均将通过它们的撤消函数正确地撤消。
b)操作系统将正确地释放线程堆栈使用的内存。
c)系统将线程的退出代码(在线程的内核对象中维护)设置为线程函数的返回值。
d)系统将递减线程内核对象的使用计数。
2.ExitThread 函数
可以让线程调用ExitThread 函数,以便强制线程终止运行:
该函数将终止线程的运行,并导致操作系统清除该线程使用的所有操作系统资源。但是,C++资源(如C++类对象)将不被撤消。由于这个原因,最好从线程函数返回,而不是通过调用ExitThread 来返回。
当然,可以使用ExitThread 的dwExitThread 参数告诉系统将线程的退出代码设置为什么。ExitThread 函数并不返回任何值,因为线程已经终止运行,不能执行更多的代码。
注意终止线程运行的最佳方法是让它的线程函数返回。但是,如果使用本节介绍的方法,应该知道ExitThread 函数是Windows用来撤消线程的函数。如果编写C/C++代码,那么决不应该调用ExitThread 。应该使用Visual C++运行期库函数_endthreadex。如果不使用Microsoft的Visual C++编译器,你的编译器供应商有它自己的ExitThread 的替代函数。不管这个替代函数是什么,都必须使用。本章后面将说明_endthreadex的作用和它的重要性。
3.TerminateThread 函数
调用TerminateThread 函数也能够终止线程的运行:
与ExitThread 不同,ExitThread 总是撤消调用的线程,而TerminateThread 能够撤消任何线程。hThread参数用于标识被终止运行的线程的句柄。当线程终止运行时,它的退出代码成为你作为dwExitThread 参数传递的值。同时,线程的内核对象的使用计数也被递减。
注意TerminateThread 函数是异步运行的函数,也就是说,它告诉系统你想要线程终止运行,但是,当函数返回时,不能保证线程被撤消。如果需要确切地知道该线程已经终止运行,必须调用WaitForSingleObject或者类似的函数,传递线程的句柄。
设计良好的应用程序从来不使用这个函数,因为被终止运行的线程收不到它被撤消的通知。线程不能正确地清除,并且不能防止自己被撤消。注意当使用返回或调用ExitThread 的方法撤消线程时,该线程的内存堆栈也被撤消。但是,如果使用TerminateThread ,那么在拥有线程的进程终止运行之前,系统不撤消该线程的堆栈。Microsoft故意用这种方法来实现TerminateThread 。如果其他仍然正在执行的线程要引用强制撤消的线程堆栈上的值,那么其他的线程就会出现访问违规的问题。如果将已经撤消的线程的堆栈留在内存中,那么其他线程就可以继续很好地运行。此外,当线程终止运行时, DLL通常接收通知。如果使用Terminate Thread 强迫线程终止,DLL就不接收通知,这能阻止适当的清除(详细信息参见第20章)。
4.在进程终止运行时撤消线程
ExitProcess和TerminateProcess函数也可以用来终止线程的运行。差别在于这些线程将会使终止运行的进程中的所有线程全部终止运行。另外,由于整个进程已经被关闭,进程使用的所有资源肯定已被清除。这当然包括所有线程的堆栈。这两个函数会导致进程中的剩余线程被强制撤消,就像从每个剩余的线程调用TerminateThread 一样。显然,这意味着正确的应用程序清除没有发生,即C++对象撤消函数没有被调用,数据没有转至磁盘等等
标题很简单,但是要讨论的地方不少;
多线程管理,在程序开发中非常重要;虽然应用比较简单,但是要用到不出错,就很难;
这里讲一下线程的退出方法;
一:简单说一下在windows上的线程启动:
方法(1): WIN32程序通用方法;
//线程:
DWORD WINAPI thread_testexit( PVOID pParam )
{
while( g_bExtiThread )
{
Sleep(1000);
static int i = 0;
CString str;str.Format( L"%d",i++);
AfxGetApp()->GetMainWnd()->SetWindowText( str );
}
return 0;
}
启动:
HANDLE hThread = ::CreateThread( 0, 0, thread_testexit, 0, NULL, NULL );
方法(2):MFC的方法
UINT thread_testexit( PVOID pParam )
{
while( g_bExtiThread )
{
Sleep(1000);
static int i = 0;
CString str;str.Format( L"%d",i++);
AfxGetApp()->GetMainWnd()->SetWindowText( str );
}
return 0;
}
启动:
CWinThread * pWinThreadtestexit = AfxBeginThread( thread_testexit, 0 );
二:线程的退出:
(1):
我想大部分人为了图方便,会定义一个BOOL变量如: BOOL g_bExtiThread ; 当 if( g_bExtiThread == 0 )的时候跳出线程循环,结束线程;
既:g_bExtiThread = 0;
只做这一步,会隐藏一个问题, 如果线程执行的时间较长,如循环中Sleep(1000); 这样会导致,执行 g_bExtiThread = 0; 后立即执行后面的函数,而不会等待线程结束;
如果线程中的变量鱼g_bExtiThread = 0;后面的执行相关,就可能隐藏问题;
(2):
这里自然,有人会用一个简单的方法避免这个过程就是:
g_bExtiThread = 0;
Sleep(2000);
这样, 等待线程结束后,执行其他语句;
这样做,有两个问题:1.效率上比较低,因为即使1000毫秒结束了,可是,却要等待Sleep(2000);
2.如果上述修改为:
g_bExtiThread = 0;
Sleep(1001);
这样看着不错,但是,如果线程中,Sleep (1000),软后语句执行的时间,大于1毫秒,也就是说 线程循环一次的时间 大于1001毫秒,仍然可能导致 “(1)”中的问题;
或者,线程很多,当线程执行到某一段的时候, CPU的时间片分配给其他线程,这样,依然会导致 线程中的循环时间无法确定;
想通过Sleep的方法等待线程循环结束,只有将时间给的很长,但是这样太浪费时间和效率了。
(3):
比较通用的方法:
通过WaitForSingleObject获取线程状态,如果线程退出,执行后面的语句;
g_bExtiThread = 0;
WaitForSingleObject( pWinThreadtestexit->m_hThread, INFINITE );
示例:
BOOL g_bExtiThread = TRUE;
CWinThread * pWinThreadtestexit = NULL;
UINT thread_testexit( PVOID pParam )
{
while( g_bExtiThread )
{
Sleep(1000);
static int i = 0;
CString str;str.Format( L"%d",i++);
//AfxGetApp()->GetMainWnd()->SetWindowText( str );
}
return 0;
}
void Ctmfc1Dlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
pWinThreadtestexit = AfxBeginThread( thread_testexit, 0 );
}
void Ctmfc1Dlg::OnBnClickedButton2()
{
// TODO: 在此添加控件通知处理程序代码
g_bExtiThread = 0;
WaitForSingleObject( pWinThreadtestexit->m_hThread, INFINITE );
SetWindowText( L"线程已经停止" );
}
这里,可能有人已经注意到了, //AfxGetApp()->GetMainWnd()->SetWindowText( str ); 我屏蔽了这句话;
这个问题留给读者,这里不再赘述了。
(4):
BOOL TerminateThread(
HANDLE hThread, // handle to thread
DWORD dwExitCode // exit code
);
这里推荐一篇文章:
TerminateThread is a dangerous function that should only be used in the most extreme cases. You should call TerminateThread only if you know exactly what the target thread is doing, and you control all of the code that the target thread could possibly be running at the time of the termination. For example, TerminateThread can result in the following problems:
If the target thread owns a critical section, the critical section will not be released.(未释放互斥区,造成死锁)
If the target thread is allocating memory from the heap, the heap lock will not be released.(未释放堆分配锁,造成死锁)
If the target thread is executing certain kernel32 calls when it is terminated, the kernel32 state fo the thread’s process could be inconsistent.(在执行内核函数时退出,造成该线程所在进程状态不确定,程序可能崩溃)
If the target thread is manipulating the global state of a shared DLL, the state of the DLL could be destroyed, affecting other users of the DLL.(在使用DLL时退出,造成DLL被销毁,其他使用该DLL得程序可能出现问题!)
A thread cannot protect itself against TerminateThread, other than by controlling access to its handles. The thread handle returned by the CreateThread and CreateProcess functions has THREAD_TERMINATE access, so any caller holding one of these handles can terminate your thread.
听过无数次不要TerminateThread,只是工作中常用,貌似也没有什么问题。今天在高强度测试中发现了一个不可原谅的错误。参看下面的例子
DWORD __stdcall mythread(void* )
{
while( true )
{
char* p = new char[1024];
delete p;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE h = CreateThread(NULL, 0, mythread, NULL, 0, NULL);
Sleep(1000);
TerminateThread(h, 0);
h = NULL;
char* p = new char[1024]; //这里会死锁,过不去
delete []p;
return 0;
}
为什么死锁呢?new操作符用的是小块堆,整个进程在分配和回收内存时,都要用同一把锁。如果一个线程在占用该锁时被杀死(即临死前该线程在new或delete操作中),其他线程就无法再使用new或delete了,表现为hang住。
《核心编程》里明确提醒不要TerminateThread,但原因并不是血淋淋滴。今天发现的这个bug印证了此书的价值。
另注:许多临时的网络操作经常用TerminateThread,作为网络不通时的退出机制,以后要改改了。比如让该线程自生自灭,自行退出。
再推荐一篇文章:
CloseHandle(),TerminateThread(),ExitThread()的区别
线程的handle用处:
线程的handle是指向“线程的内核对象”的,而不是指向线程本身.每个内核对象只是内核分配的一个内存块,并且只能由内核访问。该内存块是一种数据结构,它的成员负责维护对象的各种信息(eg: 安全性描述,引用计数等)。
CloseHandle()
在CreateThread成功之后会返回一个hThread的handle,且内核对象的计数加1,CloseHandle之后,引用计数减1,当变为0时,系统删除内核对象。
但是这个handle并不能完全代表这个线程,它仅仅是线程的一个“标识”,系统和用户可以利用它对相应的线程进行必要的操纵。如果在线程成功创建后,不再需要用到这个句柄,就可以在创建成功后,线程退出前直接CloseHandle掉,但这并不会影响到线程的运行。
不执行CloseHandle() 带来的后果:
若在线程执行完之后,没有通过CloseHandle()将引用计数减1,在进程执行期间,将会造成内核对象的泄露,相当与句柄泄露,但不同于内存泄露, 这势必会对系统的效率带来一定程度上的负面影响。但是,请记住,当进程结束退出后,系统仍然会自动帮你清理这些资源。但是在这里不推荐这种做法,毕竟不是 一个良好的编程习惯!
( 应用程序运行时,有可能泄露内核对象,但是当进程终止运行时,系统能确保所有内容均被正确地清除。另外,这个情况是用于所有对象,资源和内存块,也就是说,当进程终止时,系统将保证不会留下任何对象。)
TerminateThread()
函数的声明如下:
BOOL TerminateThread( HANDLE hThread, DWORD dwExitCode);
作用:
在线程外终止一个线程,用于强制终止线程。
参数说明:
HANDLE htread:被终止的线程的句柄,为CWinThread指针。
DWORD dwExitCode:退出码。
返回值:
函数执行成功则返回非零值,执行失败返回0。调用getlasterror获得返回的值。
听过无数次不要TerminateThread,只是工作中常用,貌似也没有什么问题。今天在高强度测试中发现了一个不可原谅的错误。参看下面的例子
[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
DWORD __stdcall mythread(void*)
{
while( true )
{
char* p = new char[1024];
delete [] p;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE h = CreateThread(NULL, 0, mythread, NULL, 0, NULL);
Sleep(1000);
TerminateThread(h , 0);
h = NULL;
char* p = new char[1024]; // 这里会死锁, 过不去
delete [] p;
return 0;
}
[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
DWORD __stdcall mythread(void*)
{
while( true )
{
char* p = new char[1024];
delete [] p;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE h = CreateThread(NULL, 0, mythread, NULL, 0, NULL);
Sleep(1000);
TerminateThread(h , 0);
h = NULL;
char* p = new char[1024]; // 这里会死锁, 过不去
delete [] p;
return 0;
}
为什么死锁呢?new操作符用的是小块堆,整个进程在分配和回收内存时,都要用同一把锁。如果一个线程在占用该锁时被杀死(即临死前该线程在new或delete操作中),其他线程就无法再使用new或delete了,表现为hang住。
<核心编程>里明确提醒不要TerminateThread,但原因并不是血淋淋滴。今天发现的这个bug印证了此书的价值。
另注:许多临时的网络操作经常用TerminateThread,作为网络不通时的退出机制,以后要改改了。比如让该线程自生自灭,自行退出。
ExitThread是推荐使用的结束一个线程的方法,当调用该函数时,当前线程的栈被释放,然后线程终止,相对于TerminateThread函数来说,这样做能够更好地完成附加在该线程上的DLL的清除工作
终止线程两个函数:ExitThread() 和 TerminateThread()
若要终止线程的运行,可以使用下面四种的方法:
线程函数退出循环来返回 (最佳方法 )。
通过调用ExitThread 函数,线程将自行撤消(尽量不要使用这种方法 )。
同一个进程或另一个进程中的线程调用TerminateThread 函数(最好避免使用这种方法 )。
该线程的主进程终止运行(避免使用 )。
下面将介绍终止线程运行的方法,并且说明线程终止运行时会出现什么情况。
1.线程函数返回
始终都应该将线程设计成这样的形式,即当想要线程终止运行时,它们就能够返回。这是
确保所有线程资源被正确地清除的唯一办法。
如果线程能够返回,就可以确保下列事项的实现:
a) 在线程函数中创建的所有C + +对象均将通过它们的撤消函数正确地撤消。
b)操作系统将正确地释放线程堆栈使用的内存。
c)系统将线程的退出代码(在线程的内核对象中维护)设置为线程函数的返回值。
d)系统将递减线程内核对象的使用计数。
2.ExitThread 函数
可以让线程调用ExitThread 函数,以便强制线程终止运行:
该函数将终止线程的运行,并导致操作系统清除该线程使用的所有操作系统资源。但是,C++资源(如C++类对象)将不被撤消。由于这个原因,最好从线程函数返回,而不是通过调用ExitThread 来返回。
当然,可以使用ExitThread 的dwExitThread 参数告诉系统将线程的退出代码设置为什么。ExitThread 函数并不返回任何值,因为线程已经终止运行,不能执行更多的代码。
注意终止线程运行的最佳方法是让它的线程函数返回。但是,如果使用本节介绍的方法,应该知道ExitThread 函数是Windows用来撤消线程的函数。如果编写C/C++代码,那么决不应该调用ExitThread 。应该使用Visual C++运行期库函数_endthreadex。如果不使用Microsoft的Visual C++编译器,你的编译器供应商有它自己的ExitThread 的替代函数。不管这个替代函数是什么,都必须使用。本章后面将说明_endthreadex的作用和它的重要性。
3.TerminateThread 函数
调用TerminateThread 函数也能够终止线程的运行:
与ExitThread 不同,ExitThread 总是撤消调用的线程,而TerminateThread 能够撤消任何线程。hThread参数用于标识被终止运行的线程的句柄。当线程终止运行时,它的退出代码成为你作为dwExitThread 参数传递的值。同时,线程的内核对象的使用计数也被递减。
注意TerminateThread 函数是异步运行的函数,也就是说,它告诉系统你想要线程终止运行,但是,当函数返回时,不能保证线程被撤消。如果需要确切地知道该线程已经终止运行,必须调用WaitForSingleObject或者类似的函数,传递线程的句柄。
设计良好的应用程序从来不使用这个函数,因为被终止运行的线程收不到它被撤消的通知。线程不能正确地清除,并且不能防止自己被撤消。注意当使用返回或调用ExitThread 的方法撤消线程时,该线程的内存堆栈也被撤消。但是,如果使用TerminateThread ,那么在拥有线程的进程终止运行之前,系统不撤消该线程的堆栈。Microsoft故意用这种方法来实现TerminateThread 。如果其他仍然正在执行的线程要引用强制撤消的线程堆栈上的值,那么其他的线程就会出现访问违规的问题。如果将已经撤消的线程的堆栈留在内存中,那么其他线程就可以继续很好地运行。此外,当线程终止运行时, DLL通常接收通知。如果使用Terminate Thread 强迫线程终止,DLL就不接收通知,这能阻止适当的清除(详细信息参见第20章)。
4.在进程终止运行时撤消线程
ExitProcess和TerminateProcess函数也可以用来终止线程的运行。差别在于这些线程将会使终止运行的进程中的所有线程全部终止运行。另外,由于整个进程已经被关闭,进程使用的所有资源肯定已被清除。这当然包括所有线程的堆栈。这两个函数会导致进程中的剩余线程被强制撤消,就像从每个剩余的线程调用TerminateThread 一样。显然,这意味着正确的应用程序清除没有发生,即C++对象撤消函数没有被调用,数据没有转至磁盘等等
发表评论
-
manifest
2015-06-08 17:57 1922我遇到的问题: 将 Upload.exe Upload.exe ... -
log4CPP多线程支持的缺角(一)(转)
2015-06-01 10:59 1038log4CPP多线程支持的缺角 ... -
minizip压缩文件夹(转)
2015-06-01 10:26 2307#include <atlconv.h> / ... -
文件大小(转)
2015-06-01 10:08 518方法一: WIN32_FIND_DATA fi ... -
log4cpp环境变量
2015-05-13 15:32 700log4cplus.appender.DEBUG_MSG.Fi ... -
linux设置环境变量putenv() setenv() getenv()(转)
2015-05-13 15:28 1255linux设置环境变量putenv() setenv() ge ... -
VC学习:log4cpp
2015-05-12 15:37 831基于LGPL开源项目 Log4cpp ... -
VC异常:File:write.c Line:67
2015-05-12 15:29 767VC异常:File:write.c Line:67 路径没找 ... -
C++异常:rethrow【转】
2015-05-08 15:20 449C++异常rethrow【转】 http://se.csai. ... -
VC异常:Free Heap block xxxxxxxx modified at xxxxxxxx after it was freed(转)
2015-05-08 15:18 1245Free Heap block xxxxxxxx modifi ... -
VC异常:Free Heap block XXXXXX modified at XXXXXX after it was freed(转)
2015-05-07 15:41 3122Free Heap block XXXXXX modified ... -
VC中关于 0xcccccccc、0xcdcdcdcd和 0xfeeefeee 异常值说明
2015-05-07 15:14 1200VC中关于 0xcccccccc、0xcdcdcdcd和 0x ... -
析构函数后,还可以调用函数成员
2015-05-07 15:13 919析构函数后,还可以调用函数成员 类的所有的函数成员的是编译时期 ... -
[size=medium] c,c#判断socket是否断开[/size]
2015-03-17 13:28 1126c,c#判断socke ... -
时间转换
2015-01-08 11:55 514时间转换 public static long DateT ... -
C++ localtime函数需要注意的地方
2015-01-08 11:42 1031time_t tLast = pLastFQKLineIt ... -
TerminateThread
2014-11-13 14:04 722TerminateThread. 如线程内部不涉及空间开辟, ... -
创建文件夹CreateDirectoryA(file, 0);
2014-10-23 17:19 1546创建文件夹CreateDirectoryA(file, 0) ... -
heap corruption detected错误解决方法调试方法以及内存管理相关(转载)
2014-10-23 16:57 1213heap corruption detected错误解决方法调 ... -
edit control字体背景颜色
2014-10-23 16:56 746edit control字体背景颜色 属性必须 readon ...
相关推荐
内容概要:本文详细介绍了基于MATLAB/Simulink的电动助力转向系统(EPS)模型的构建及其控制方法。首先,文中阐述了EPS在提升驾驶体验和安全性方面的重要意义。接着,重点讲解了四个关键模型的搭建:整车二自由度模型用于研究车辆转向特性;助力特性曲线模型确定不同驾驶条件下助力电机提供的助力力矩;助力电机模型模拟助力电机的工作过程;齿条模型描述助力电机转矩转化为车轮转向的动作。每个模型都有具体的参数设定和代码示例。此外,文章还解释了模型的输入(如前轮转角、方向盘力矩)和输出(转向助力力矩),并指出控制方法基于各模型间的输入输出关系,利用基本数学公式和逻辑判断实现。 适用人群:汽车工程领域的研究人员、工程师和技术爱好者。 使用场景及目标:适用于希望深入了解EPS工作原理的研究人员,以及需要进行EPS系统设计和优化的工程师。目标是掌握EPS系统的建模方法和控制策略,为实际项目提供理论支持和技术指导。 其他说明:文中提供了丰富的代码片段和详细的模型介绍,有助于读者更好地理解和实践。同时强调了EPS对于提高驾驶安全性和舒适性的重要性。
实训商业源码-帝国cms7.5 7.2 UTF-8移动端同步插件-酷网站-论文模板.zip
内容概要:本文详细介绍了基于Lasso分位数回归的数据回归预测方法。首先阐述了Lasso分位数回归作为一种结合Lasso回归与分位数回归的统计方法,能够在处理变量选择和模型复杂度方面发挥重要作用。接着解释了其基本原理,即在分位数回归基础上加入Lasso正则化项,从而确保模型既能良好拟合数据,又能有效避免过拟合现象。随后讨论了具体实施流程,从数据预处理到最终预测,涵盖了特征选择、模型构建以及参数优化等多个环节。最后强调了该方法在多个行业(如金融、医疗)的实际应用场景及其潜在价值。 适合人群:对统计学、机器学习有一定了解的研究人员和技术爱好者。 使用场景及目标:适用于需要精确预测并同时考虑多维度因素影响的场合,特别是在面对高维数据时,希望通过减少冗余变量来提高预测准确性的情况。 其他说明:文中提到的方法不仅限于特定领域,而是可以在多种不同类型的预测任务中发挥作用,为决策提供科学依据。
这段代码实现了一个 三维状态的扩展卡尔曼滤波 (Extended Kalman Filter, EKF) 算法。通过生成过程噪声和观测噪声,对真实状态进行滤波估计,同时对比了滤波前后状态量的误差和误差累积分布曲线。 只有一个m文件,下载后使用MATLAB打开运行即可,带误差输出。
毕业设计-百川多公众号集字福袋 2.0.5开源-整站商业源码.zip
实训商业源码-多商家营销活动平台V1.3.9小程序前后端完整全开源解密源码-论文模板.zip
ISC大作业论文
毕业论文-在线进销存-整站商业源码.zip
毕业设计-步数宝步数换购小程序 7.8.1-整站商业源码.zip
实训商业源码-叮咚-门店会员卡小程序4.8.2开源-论文模板.zip
毕业论文-芸众圈子社区V1.7.6 开源版-整站商业源码.zip
内容概要:本文探讨了多智能体强化学习(MARL)在配电网有功电压控制中的应用。文中介绍了将电压约束转化为势垒函数的方法,并在Dec-POMDP框架下对七种最先进的MARL算法进行了大规模实验。实验表明,设计合理的电压势垒函数对于提高电压控制效果至关重要。此外,作者还建立了开源环境,旨在促进电力社区和MARL社区的合作,推动MARL算法的实际应用。 适合人群:从事电力系统自动化、智能电网研究的专业人士,以及对多智能体系统和强化学习感兴趣的科研人员。 使用场景及目标:适用于需要优化配电网电压控制的场景,特别是希望通过软件手段而非硬件升级来提升电力质量和缓解电力拥塞的情况。目标是展示MARL在电力系统中的潜力,并为后续研究提供工具和支持。 其他说明:文章不仅讨论了理论和技术细节,还包括大量代码片段,帮助读者理解和实践MARL在电压控制中的具体应用。
内容概要:本文基于PFC3D(Particle Flow Code 3D)软件,详细探讨了岩石注浆过程中的破坏现象及其背后的机理。首先介绍了注浆破坏的复杂性,指出这是由材料特性、地质构造和计算机模拟技术共同决定的。接着重点讲解了注浆速度和流量的调整方法,强调适当的速度和流量对于确保注浆效率和避免过度破坏的重要性。最后讨论了在不考虑渗流场的情况下,如何根据岩石结构特征选择最佳的注浆孔位置,以提高注浆效果并保护周围岩石结构。 适合人群:从事地质工程领域的研究人员和技术人员,尤其是那些希望深入了解岩石注浆过程的人。 使用场景及目标:适用于需要利用PFC3D进行岩石注浆模拟的研究项目,旨在帮助用户掌握注浆速度、流量调节技巧以及合理的注浆孔位选择方法。 其他说明:文中提供了简单的PFC3D模拟代码框架,便于读者快速上手实践。同时提醒读者注意实际操作时应结合实验室理论模型和现场具体情况来进行参数优化。
内容概要:本文详细介绍了IEEE标准节点仿真模型系列,涵盖了从简单到复杂的多个节点配置,如2机5节点、6节点、3机9节点、13节点、5机14节点、15节点、30节点、33节点、34节点、10机39节点以及69节点。所有模型均已成功调试并实现了潮流计算,适用于短路仿真、稳定性研究和电能质量研究等领域。文中还特别强调了三相等效电源的应用,这是模拟真实电力系统的关键要素之一。 适合人群:从事电力系统研究、仿真和优化的专业人士和技术人员。 使用场景及目标:①用于电力系统短路仿真的建模与分析;②评估电力系统的稳定性和可靠性;③研究电能质量问题,提升电力设备的运行效率和寿命。 阅读建议:本文提供了丰富的背景知识和具体应用场景,建议读者结合实际项目需求选择合适的模型进行深入研究和应用。
实训商业源码-【超人】积分商城 5.2.26-论文模板.zip
实训商业源码-思创兼职小程序V6.7.6 开源版-论文模板.zip
2025年手绘风格毕业设计答辩模板范文
内容概要:本文档详细介绍了使用C语言实现常用的数据结构和算法。首先阐述了算法与数据结构的重要性,并具体讲解了链表、栈、队列、二叉树、图等数据结构的实现方法及其操作函数。接着深入探讨了快速排序和二分查找这两种高效的排序与查找算法,提供了完整的代码示例并解释了每个部分的作用。最后还讨论了图结构的深度优先搜索(DFS)和广度优先搜索(BFS)遍历算法,强调了内存管理和防御性编程的重要性。所有代码示例均可直接编译运行,建议在Linux环境下使用gcc编译测试。 适合人群:具备一定编程基础,尤其是熟悉C语言的初学者或有一定经验的研发人员。 使用场景及目标:①帮助读者理解并掌握常见的数据结构(如链表、栈、队列、二叉树、图)及其基本操作;②通过实际编码练习提高读者对经典算法(如快速排序、二分查找)的理解;③培养良好的编程习惯,如内存管理和防御性编程。 阅读建议:由于文档包含大量代码片段和详细的实现步骤,读者应边阅读边动手实践,尝试编译和运行提供的代码示例,同时注意理解每段代码背后的逻辑和设计思想。此外,建议读者关注文档中提到的编程规范和最佳实践,以提升自身的编程技能。
毕业论文-源导航V1.0-整站商业源码.zip
毕业论文-咻一咻抽奖V4.3.1 开源版-整站商业源码.zip