C语言: 多线程(2)-线程的停止和创建

函数:_endthread() 和 _endthreadex()

_endthread是自动删除线程句柄,而_beginthreadex 是需要在结束线程后手动关闭句柄。

终止线程:_endthread终止由 _beginthread创建的线程,并 _endthreadex终止由 _beginthreadex创建的线程。

可以显式调用 _endthread或 _endthreadex以终止线程;但是,当线程从作为参数传递给 _beginthread或 _beginthreadex的例程中返回时,会自动调用 _endthread或 _endthreadex 。 使用对endthread或 _endthreadex的调用来终止线程有助于确保正确恢复为线程分配的资源。

对于与 Libcmt.lib 链接的可执行文件,请不要调用 Win32 ExitThread API;这将阻止运行时系统回收已分配的资源。 _endthread和 _endthreadex回收分配的线程资源,然后调用ExitThread

_endthread会自动关闭线程句柄。 (此行为与 Win32 ExitThread API 不同。)因此,使用 _beginthread和 _endthread时,不要通过调用 Win32 CloseHandle API 来显式关闭线程句柄。

与 Win32 ExitThread API 一样, _endthreadex不会关闭线程句柄。 因此,在使用 _beginthreadex和 _endthreadex时,必须通过调用 Win32 CloseHandle API 来关闭线程句柄。

注意:_endthread和 _endthreadex导致C++不会调用线程中的析构函数。

 语法:

void _endthread( void );
void _endthreadex(
   unsigned retval//线程退出代码。
);

创建线程函数:CreateThread:

HANDLE CreateThread(
  LPSECURITY_ATTRIBUTES   lpThreadAttributes,
  SIZE_T                  dwStackSize,
  LPTHREAD_START_ROUTINE  lpStartAddress,
  __drv_aliasesMem LPVOID lpParameter,
  DWORD                   dwCreationFlags,
  LPDWORD                 lpThreadId
);

参数:

  1. lpThreadAttributes:指向SECURITY_ATTRIBUTES 结构的指针,该结构确定子进程是否可以继承返回的句柄。如果 lpThreadAttributesNULL,则无法继承句柄。结构的lpSecurityDescriptor成员为新线程指定安全描述符。如果lpThreadAttributes为NULL,则线程获取默认安全描述符。线程的默认安全描述符中的ACL来自创建者的主要标志。
  2. dwStackSize:堆栈的初始大小,以字节为单位。系统将此值四舍五入到最近的页面。如果此参数为零,则新线程使用可执行文件的默认大小。
  3. lpStartAddress:指向由线程执行的应用程序定义函数的指针。该指针表示线程的起始地址。有关线程函数的更多信息,请参见 ThreadProc(回调函数)
  4. lpParameter:指向要传递给线程的变量的指针。
  5. dwCreationFlags:控制线程创建的标志。0:该线程在创建后立即运行;CREATE_SUSPENDEDs 0x00000004:线程是在挂起状态下创建的,并且在调用ResumeThread函数之前不会运行 ;STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000 :所述dwStackSize参数指定堆栈的初始保留大小。如果未指定此标志,则dwStackSize指定提交大小。
  6. lpThreadId:指向接收线程标识符的变量的指针。如果此参数为 NULL,则不返回线程标识符。

返回值:

如果函数成功,则返回值是新线程的句柄。如果函数失败,则返回值为NULL。要获取扩展错误信息,请调用 GetLastError

详细信息:MSDN说明

示例:

#include<stdio.h>
#include<stdlib.h>
#include<process.h>//线程头文件
#include<Windows.h>
#include<time.h>


DWORD WINAPI function(void* vPtr)
{
	int* p = (int*)vPtr;
	while (1)
	{
		printf("%d\n", (*p)++);

		if (*p > 200)
		{
			_endthread();//在函数内部调用退出线程函数
		}
		Sleep(300);
	}

	return 0;
}


//主线程,管理和调用其他线程,起主导作用
void main()
{
	int n = 100;
	HANDLE hd = CreateThread(NULL, 0, function, &n, 0, NULL);//创建线程执行函数


	system("pause");
}

线程的冻结和解冻:

SuspendThread函数:挂起指定的线程。64位应用程序可以使用Wow64SuspendThread函数挂起WOW64线程。

语法:

DWORD SuspendThread(
  HANDLE hThread
);

参数:hThread 要挂起的线程的句柄。句柄必须具有THREAD_SUSPEND_RESUME访问权限。有关更多信息,请参见 线程安全性和访问权限。

返回值:如果函数成功,则返回值是线程的先前挂起计数;否则为(DWORD) -1。若要获取扩展的错误信息,请使用GetLastError函数。

如果函数成功,则中止指定线程的执行,并且线程的中止计数增加。挂起线程会使线程停止执行用户模式(应用程序)代码。

此功能主要是供调试器使用的。它不打算用于线程同步。在拥有同步对象(例如互斥锁或关键节)的线程上调用SuspendThread会导致死锁,前提是该调用线程试图获取挂起线程拥有的同步对象。为了避免这种情况,不是调试器的应用程序中的线程应向另一个线程发出信号以挂起自身。目标线程必须设计为监视此信号并适当响应。

每个线程都有一个暂停计数(最大值为MAXIMUM_SUSPEND_COUNT)。如果挂起计数大于零,则线程被挂起;否则,线程被挂起。否则,该线程不会被挂起并且可以执行。调用 SuspendThread会使目标线程的挂起计数增加。尝试增加超过最大挂起计数会导致错误,而不会增加计数。

 

ResumeThread函数:减少线程的挂起计数。当挂起计数递减为零时,将继续执行线程。

语法:

DWORD ResumeThread(
  HANDLE hThread
);

参数:hThread 要挂起的线程的句柄。句柄必须具有THREAD_SUSPEND_RESUME访问权限。

返回值:如果函数成功,则返回值是线程的先前挂起计数。如果函数失败,则返回值为(DWORD)-1。要获取扩展的错误信息,请调用GetLastError

该 ResumeThread函数检查挂起的主题线程数。如果挂起计数为零,则线程当前未挂起。否则,主题线程的挂起计数将减少。如果结果值为零,则继续执行主题线程。

如果返回值为零,则指定的线程没有被挂起。如果返回值为1,则指定的线程已挂起但已重新启动。如果返回值大于1,则指定的线程仍被挂起。

请注意,在报告调试事件时,报告过程中的所有线程均被冻结。调试器应使用 SuspendThread和 ResumeThread函数来限制可以在进程中执行的线程集。通过挂起一个进程中除报告调试事件的线程以外的所有线程,可以“单步执行”单个线程。如果其他线程被挂起,则它们不会通过继续操作释放。

Windows Phone 8.1:   Windows Phone 8.1及更高版本上的Windows Phone Store应用程序支持此功能。

Windows 8.1Windows Server 2012 R2:Windows 8.1,Windows Server 2012 R2和更高版本上的Windows Store应用程序支持此功能。

示例:

#include<stdio.h>
#include<stdlib.h>
#include<process.h>//线程头文件
#include<Windows.h>
#include<time.h>


DWORD WINAPI function(void* vPtr)
{
	int* p = (int*)vPtr;
	while (1)
	{
		printf("%d\n", (*p)++);

		if (*p > 200)
		{
			_endthread();//在函数内部调用退出线程函数
		}
		Sleep(300);
	}

	return 0;
}

void main()
{
	int n = 100;
	HANDLE hd = CreateThread(NULL, 0, function, &n, 0, NULL);//创建线程执行函数
	system("pause");
	if (hd != 0)
	{
		double d1= SuspendThread(hd);//冻结线程
		if (d1 == 0)
		{
			printf("线程冻结成功\n");
		}
		else
		{
			double d2=GetLastError();
		}
	}
	
	system("pause");
	if (hd != 0)
	{
		double d1 = ResumeThread(hd);//解冻线程
		if (d1 == 1)
		{
			printf("线程解冻成功\n");
		}
		else
		{
			double d2 = GetLastError();
		}
	}
	

	system("pause");
}

TerminateThread函数:终止线程。强制终止线程

BOOL TerminateThread(
  HANDLE hThread, //要终止的线程的句柄。
  DWORD  dwExitCode//线程的退出代码。使用 GetExitCodeThread函数检索线程的退出值。
);

如果函数成功,则返回值为非零。如果函数失败,则返回值为零。要获取扩展的错误信息,请调用 GetLastError

TerminateThread用于导致线程退出。发生这种情况时,目标线程将没有机会执行任何用户模式代码。不会通知连接到线程的DLL线程正在终止。系统释放线程的初始堆栈。

Windows Server 2003和Windows XP:  目标线程的初始堆栈未释放,导致资源泄漏。

TerminateThread是一个危险函数,仅应在最极端的情况下使用。仅当您确切知道目标线程在做什么时,才应调用TerminateThread,并控制终止时目标线程可能正在运行的所有代码。例如, TerminateThread可能导致以下问题:

  • 如果目标线程拥有一个关键部分,则该关键部分将不会被释放。
  • 如果目标线程正在从堆中分配内存,则不会释放堆锁。
  • 如果目标线程在终止时正在执行某些kernel32调用,则该线程进程的kernel32状态可能不一致。
  • 如果目标线程正在操纵共享DLL的全局状态,则DLL的状态可能会被破坏,从而影响DLL的其他用户。

线程只能通过控制对其句柄的访问来保护自己免受TerminateThread的侵害 。由CreateThread和 CreateProcess函数返回的线程句柄 具有THREAD_TERMINATE访问权限,因此任何持有这些句柄之一的调用方都可以终止您的线程。

如果在调用此函数时目标线程是进程的最后一个线程,则该线程的进程也会终止。

线程对象的状态将发出信号,释放所有其他等待线程终止的线程。线程的终止状态从STILL_ACTIVE更改为dwExitCode参数的值。

终止线程并不一定要从系统中删除线程对象。当最后一个线程句柄关闭时,将删除线程对象。

 

 

 

 

 

 

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值