接口说明:
参考资料:
https://devblogs.microsoft.com/oldnewthing/?p=91811
对注意事项进行翻译:
TerminateThread is used to cause a thread to exit. When this occurs, the target thread has no chance to execute any user-mode code. DLLs attached to the thread are not notified that the thread is terminating. The system frees the thread's initial stack.
TerminateThread被用于杀进程,接口被调用后,目标线程没有机会去执行任何用户态代码, DLL也无法感知该线程退出,系统会释放线程的栈空间
Windows Server 2003 and Windows XP: The target thread's initial stack is not freed, causing a resource leak.
以前的Windows2003和XP版本,不会释放栈空间,会导致资源泄漏。
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 for 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.
1. 如果线程正持有临界区资源(比如锁、信号量等),该临界区资源将不会被释放。
2. 如果线程正在分配内存被杀,操作系统的heaplock不会被释放,即有可能其他线程无法再分配到内存 (TODO)
3. 如果线程正在执行kernel32调用,kernel32记录的线程的进程信息,状态可能出现不一致(TODO)
4. 如果线程正在操作共享DLL的全局状态,则DLL的全局状态会被破坏,进而影响该DLL的其他用户(TODO)
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.
除非控制对线程自身句柄的访问,否则它无法防止别人调用该接口。通过CreateThread和CreateProcess函数创建出来的线程,只要用户持有该句柄,就可以杀掉这个线程。
If the target thread is the last thread of a process when this function is called, the thread's process is also terminated.
如果当前线程是进程的最后一个线程,被杀后进程也会死。
The state of the thread object becomes signaled, releasing any other threads that had been waiting for the thread to terminate. The thread's termination status changes from STILL_ACTIVE to the value of the dwExitCode parameter.
杀死当前进程会使它的状态从STILL_ACTIVE变为退出状态(具体的值取决于杀进程时用户传入的参数),因此其他等待该线程退出的代码会被激活。
Terminating a thread does not necessarily remove the thread object from the system. A thread object is deleted when the last thread handle is closed.
终止线程并不一定会从系统中移除该线程对象,只有关闭最后一个线程句柄的时候才会释放线程对象 (TODO)
总结
- 如果是通过c++ 11的
thread
创建的线程对象,那么杀掉它维护的线程时,不要再让该thread对象析构,否则必定crash - 如果该线程正持有锁、信号量等临界区资源,杀掉该线程会导致其他使用临界区资源的线程卡死,从API说明的注意事项以及实测来看,加锁、内存申请、日志打印(会加文件锁)等都可能会出现问题,因此一定要谨慎使用该接口。
个人认为,只有在当前出现BUG已经卡死的情况下,可以尝试使用该接口快速卸载DLL或退出进程,避免卡死。
附自测代码
void SleepInThread()
{
cout << "Start run" << endl;
this_thread::sleep_for(chrono::milliseconds(10000));
cout << "End run" << endl;
}
class TestClass
{
public:
void operator()() {
SleepInThread();
}
static void TestStaticFunc()
{
SleepInThread();
}
void TestMemFunc()
{
SleepInThread();
}
};
int32_t KillProcess(thread *th)
{
HANDLE hd= th->native_handle();
std::cout << "Terminate thread, handle: "<<(uint64_t)(uintptr_t)hd<<" thread_id: "<<th->get_id()<<endl;
SuspendThread(hd);
int ret = TerminateThread(hd, 1);
if (ret == 0) {
DWORD rc = GetLastError();
cout << "Terminate thread failed: " << rc<<endl;
return rc;
}
return 0;
}
int TestFunction()
{
thread *th = new thread(SleepInThread);
return KillProcess(th);
}
int FreeThreadObjAgain()
{
thread *th = new thread(SleepInThread);
KillProcess(th);
cout << "start free thread" << endl;
delete th;
cout << "amazing! free thread" << endl;
return 0;
}
int32_t TestLambda()
{
thread *th = new thread([]() {
SleepInThread();
});
HANDLE thHandle = th->native_handle();
return KillProcess(th);
}
int TestCallableClass()
{
thread *th = new thread(TestClass());
return KillProcess(th);
}
int TestClassStaticFunc()
{
thread *th = new thread(&TestClass::TestStaticFunc);
return KillProcess(th);
}
int TestClassMemFunc()
{
TestClass obj;
thread *th = new thread(&TestClass::TestMemFunc, &obj);
return KillProcess(th);
}
class TestCriticalSection
{
public:
void LockAndKilled() {
lock_guard<mutex> lock(mu);
cout << "Do something start... thread:"<<this_thread::get_id() << endl;
this_thread::sleep_for(chrono::milliseconds(10000));
cout << "Do something end..." << endl;
}
void LockAndPrint()
{
cout << "Try to lock" << endl;
lock_guard<mutex> lock(mu);
cout << "Yeah!!" << endl;
}
private:
mutex mu;
};
int TestDeadLock()
{
TestCriticalSection criSec;
thread *t1 = new thread(&TestCriticalSection::LockAndKilled, &criSec);
int32_t ret = KillProcess(t1);
if (ret != 0) {
cout << "Kill failed" << endl;
return ret;
}
criSec.LockAndPrint();
return 0;
}
void TestLock(int i)
{
uint32_t count = 10000;
while (count--) {
cout <<"["<<i<<"] thread: " << this_thread::get_id() <<"Start malloc, left count: "<< count << endl;
char* mem = (char*)malloc(100000);
free(mem);
cout <<"["<<i<<"] thread: " << this_thread::get_id() << endl;
this_thread::yield();
}
}
int TestHeapLock()
{
thread *t1 = new thread(TestLock, 1);
thread *t2 = new thread(TestLock, 2);
int32_t ret = KillProcess(t1);
if (ret != 0) {
cout << "Kill failed" << endl;
return ret;
}
//this_thread::sleep_for(chrono::milliseconds(1000));
t2->join();
return 0;
}
int main()
{
std::cout << "Hello World!
" << endl;
// 杀了进程再次析构thread会crash
//FreeThreadObjAgain();
//cout << "[ERR1]Free after terminate will crash" << endl;
// 加锁被杀会导致锁永远无法加上
//ret = TestDeadLock();
//cout << "[TEST6]Terminate TestDeadLock is ok" << endl;
// 普通函数+操作【睡眠】。杀了不释放内存没问题
int32_t ret = TestFunction();
cout << "[TEST1]Terminate function is ok" << endl;
// lambda函数 + 操作【睡眠】。 杀了不释放内存
ret = TestLambda();
cout << "[TEST2]Terminate lambda is ok" << endl;
// 可调用对象
ret = TestCallableClass();
cout << "[TEST3]Terminate TestCallableClass is ok" << endl;
// 类静态函数
ret = TestClassStaticFunc();
cout << "[TEST4]Terminate TestClassStaticFunc is ok" << endl;
// 类成员函数
ret = TestClassMemFunc();
cout << "[TEST5]Terminate TestClassMemFunc is ok" << endl;
/* 实际没碰到内存申请失败的时候卡死,而总是碰到打印日志的时候lock_file卡死
ret = TestHeapLock();
cout << "[ERR]Terminate when hold heaplock" << endl;
*/
std::cout << "Bye!
";
system("pause");
}