Windows多线程初探

线程是进程中的一个执行单位(每个进程至少有一个主线程),一个进程可以有多个线程,而一个线程只存在于一个进程中。在数据关系上属于一对多的关系。线程不占有系统资源,它所使用的资源全部由所属进程向系统申请。

在多处理器中,不同的线程可以同时运行在不同的CPU上,这样可以提高程序的运行效率。除此之外,有些时候必须使用多线程。例如,杀毒软件在查杀病毒的时候,它需要一边扫描相关的磁盘文件,一边显示当前的扫描进度以及发现的问题。如果把这几个工作放在一个线程中执行,会让程序看上去像卡住一样。在这种情况下,分为多个线程,使用不同的线程完成不同的工作,就可以协同达到预想的效果。

创建线程所用的API:

HANDLE CreateThread(
  LPSECURITY_ATTRIBUTES lpThreadAttributes,//指明创建线程的安全属性,为指向SECURITY_ATTRIBUTES结构体的指针,该参数一般设置为NULL
  SIZE_T dwStackSize,//指定线程使用的堆栈大小,如果为NULL,则与主线程栈相同
  LPTHREAD_START_ROUTINE lpStartAddress,//指定线程函数,线程从该函数的入口处开始运行,函数返回时就意味着线程终止运行,该函数属于回调函数。
/*
线程回调函数的定义形式如下:
DWORD  WINAPI  ThreadProc(
    LPVOID  lpParameter
);
函数的返回值DWORD类型,该函数只有一个参数,该参数由CreateThread函数给定。该函数的函数名可以任意定义。
*/
  LPVOID lpParameter,//该参数表示传递给线程函数的一个参数,可以使指向任意数据类型的指针
  DWORD dwCreationFlags,//该参数指明创建线程后的状态,在创建线程后可以让线程立即执行,也可以让线程处于暂停状态。如果需要立即执行,设置为0;如果要让线程处于暂停状态,设置为CREATE_SUSPENED,需要线程执行时调用ResumeThread函数来即可。
  LPDWORD lpThreadId//该参数用于返回新创建线程的线程ID
);

尝试写一个多线程的例子,代码如下:

#include <windows.h>
#include <iostream>
using namespace std;

DWORD WINAPI ThreadProc(LPVOID lpParam)
{
	cout<<"In ThreadProc"<<endl;
	return 0;
}

int main()
{
	HANDLE hThread = CreateThread(NULL, 0, ThreadProc,
		NULL, 0, NULL);
//代码插入处1
	cout<<"In main"<<endl;

	CloseHandle(hThread);
	return 0;
}

运行结果1如下:

运行结果2如下:

对于第一种情况,其中只输出了“In  main”,而我们指定的线程函数并没有执行输出。这是因为主线程在CPU分给它的第一个时间片就执行完了,主线程执行完就意味着整个程序结束了,而新创建的那个线程甚至都没有执行的机会。

针对这种情况,我们可以使用如下的API使得新创建的线程有机会执行自己的线程函数。

DWORD WaitForSingleObject(
    HANDLE  hHandle,//要等待的句柄对象
    DWORD  dwMilliseconds//指定等待超时毫秒数,如果设置为0,则立即返回,如果设置为INFINITE,则表示一直等待线程函数的返回。INFINITE是系统定义的一个宏,其定义如下:
#define  INFINITE 0xFFFFFFFF
);

如果该函数失败,返回WAIT_FAILED;如果等待的对象变成激发状态,则返回WAIT_OBJECT_0;如果等待对象变成激发状态之前,等待时间结束了,则返回WAIT_TIMEOUT。

我们在代码标号1处添加如下语句:

WaitForSingleObject(hThread, INFINITE);

则其运行结果如下:

对于第二种情况,可能第一眼看上去有点晕,这输出的是什么?其实是两个线程在使用输出流缓冲区的时候交错了。大家都往输出缓冲区中添加数据,所以最好输出时是这两个线程要输出数据的“混合体”,既不是a线程要输出的数据,也不是b线程要输出的数据。针对这种情况怎么办呢?既然不能让两个线程同时使用,那就让它们分开使用就行了。

可以使用临界区来解决该问题。临界区对象是一个CRITICAL_SECTION的数据结构,Windows操作系统使用该数据结构对关键代码进行保护,以确保多线程下的共享资源可以被正确使用。在同一时间内,Windows只允许一个线程进入临界区。

操作临界区的函数有4个:

初始化临界区

VOID  InitializeCriticalSection(
    LPCRITICAL  SECTION  lpCriticalSection
);

进入临界区

VOID  EnterCriticalSection(
    LPCRITICAL  SECTION  lpCriticalSection
);

离开临界区

VOID  LeaveCriticalSection(
    LPCRITICAL  SECTION  lpCriticalSection
);

删除临界区

VOID  DeleteCriticalSection(
    LPCRITICAL  SECTION  lpCriticalSection
);

其中,这4个API的参数都是指向CRITICAL_SECTION结构体的指针。

此时,我们可以把代码修改为如下形式:

#include <windows.h>
#include <iostream>
using namespace std;

CRITICAL_SECTION g_cs;//定义临界区对象

DWORD WINAPI ThreadProc(LPVOID lpParam)
{
	EnterCriticalSection(&g_cs);//进入临界区

	cout<<"In ThreadProc"<<endl;

	LeaveCriticalSection(&g_cs);//离开临界区
	return 0;
}

int main()
{
	InitializeCriticalSection(&g_cs);

	HANDLE hThread = CreateThread(NULL, 0, ThreadProc,
		NULL, 0, NULL);

	EnterCriticalSection(&g_cs);

	cout<<"In main"<<endl;

	LeaveCriticalSection(&g_cs);

	WaitForSingleObject(hThread, INFINITE);
	CloseHandle(hThread);
	DeleteCriticalSection(&g_cs);
	return 0;
}

此时执行结果如下:



有时候,我们可能会创建不止一个线程用于其它的工作,这时候有怎么协调好它们呢?主要是两点:1.在使用公共资源的时候记得要让它们互斥使用。2.保证所有的工作做完了之后再退出程序。

对于第二点,有一个可以等待多个指定句柄的API:

DWORD  WaitForMultipleObjects(
DWORD  nCount,//用于指明想要让函数等待的线程数量。这个值需要在1和MAXIMUM_WAIT_OBJECTS之间。
CONST HANDLE *lpHandles,//指向等待线程句柄的数组指针
BOOL  fWaitAll,//表示是否等待全部线程的状态完成,如果设置为TRUE,则等待全部
DWORD  dwMilliseconds//等待超时毫秒数,与WaitForSingleObject函数中用法相同
);

则,创建多个线程的代码轮廓大致如下:

定义一个临界区;
DWORD  WINAPI  ThreadProc(LPVOID  lpParam)
{
    进入临界区
    执行在共享资源中的所需操作;
    离开临界区
    可能还有其它非临界区中的操作;
}
int main()
{
    初始化一个临界区;
    HANDLE hThread[N] = {0};
    for(int i = 0; i < N; ++i)
    {
        hThread[i] = CreateThread(…);
}
WaitForMultipleObjects(N, hThread, TRUE, INFINITE);
……//一些其它的操作
for(i = 0; i < N; ++i )
{
    CloseHandle(hThread(i));
}

删除临界区;
return 0;
}

好了,多线程方面的知识博大精深,就先分享到这儿了。



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值