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;
}

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

时间: 2024-12-21 15:45:28

Windows多线程初探的相关文章

多线程初探

一.定义 线程是操作系统可以调度的最小单位,线程被包含在进程中,是进程中实际运作的最小单位.一个进程可以只有一个线程,也可以有多个线程. 二.为什么要使用线程? 1.优化程序响应,提升用户体验,使用线程可以防止应用程序假死 2.充分使用CPU资源 三.线程的简单使用 线程用来执行进程分配的子任务,该子任务在程序中就体现在执行某一个方法.那么,线程怎么知道执行哪一个方法呢?事实上线程不知道执行哪个方法,因此线程类(Thread)中使用了委托,以达到让线程执行某个方法的目的,线程具体使用步骤如下:

Windows多线程编程总结

Windows 多线程编程总结 keyword:多线程 线程同步 线程池 内核对象 1 内核对象 1 .1 内核对象的概念 内核对象是内核分配的一个内存块,这样的内存块是一个数据结构,表示内核对象的各种特征.而且仅仅能由内核来訪问.应用程序若须要訪问内核对象,须要通过操作系统提供的函数来进行,不能直接訪问内核对象( Windows 从安全性方面来考虑的). 内核对象通过 Create* 来创建,返回一个用于标识内核对象的句柄,这些句柄 (而不是内核对象)可在创建进程范围内使用,不可以被传递到其它

windows多线程同步

概述 任何单个应用程序都不能完全使该处理器达到满负荷.当一个线程遇到较长等待时间事件时,同步多线程还允许另一线程中的指令使用所有执行单元.例如,当一个线程发生高速缓存不命中,另一个线程可以继续执行.同步多线程是 POWER5? 和 POWER6? 处理器的功能,可与共享处理器配合使用. SMT 对于商业事务处理负载的性能优化可达30%.在更加注重系统的整体吞吐量而非单独线程的吞吐量时,SMT 是一个很好地选择. 但是并非所有的应用都能通过SMT 取得性能优化.那些性能受到执行单元限制的应用,或者

Windows消息初探(1)

最近恢复对Windows API的学习,深深感受到没有对应的中文资料的痛苦,于是上MSDN上面去将Windows消息搞了一些回来翻译出来,供自己查阅,也与大家分享,能力有限,不保证一定是完全正确的,只希望能帮大家节约些时间. 消息名称 消息意义 消息数值 WM_MOVE Sent after a window has been moved.窗体被移动后触发此消息 &H3 WM_ACTIVATEAPP 当别的窗体被激活 &H1C WM_CANCELMODE 当当前窗口今进入模态时,窗口收到这

windows多线程接口介绍和使用

一windows多线程接口: 1 创建线程 CreateThread 与 _beginthreadex都可以实现创建线程,两个函数的参数 相同, HANDLEWINAPICreateThread( LPSECURITY_ATTRIBUTESlpThreadAttributes, SIZE_TdwStackSize, LPTHREAD_START_ROUTINElpStartAddress, LPVOIDlpParameter, DWORDdwCreationFlags, LPDWORDlpThr

Windows多线程编程及常见问题

提要: Windows 多线程Helloworld 以Windows代码为例,分析多线程编程中易出现的问题 Windows多线程的Helloworld: 笔者写过Java多线程的程序(实现Runnable接口,利用Thread类执行),也写过Linux多线程程序(利用pthread).最近由于另有需要使用Windows多线程,由于Windows API历来难用,特此记录,以作备忘. Helloworld源代码如下: 1 #include <stdio.h> 2 #include <win

Windows多线程多任务设计初步(转)

Windows多线程多任务设计初步 [前言:]当前流行的Windows操作系统,它能同时运行几个程序(独立运行的程序又称之为进程),对于同一个程序,它又可以分成若干个独立的执行流,我们称之为线程,线程提供了多任务处理的能力.用进程和线程的观点来研究软件是当今普遍采用的方法,进程和线程的概念的出现,对提高软件的并行性有着重要的意义.现在的应用软件无一不是多线程多任务处理,单线城的软件是不可想象的.因此掌握多线程多任务设计方法对每个程序员都是必需要掌握的.本文针对多线程技术在应用中经常遇到的问题,如

【APUE】关于windows多线程编程的学习笔记

保证在某一时刻只有一个线程对数据进行操作的基本方法: (1)关中断:通过关闭时钟中断来停止线程调度(不现实) (2)数学互斥方法:Peterson算法 bakery算法 (3)操作系统提供的互斥方法:临界区.互斥量.信号量等(windows) (4)cpu原子操作:把一些常用的指令设计成了原子指令,在windows上面也被称为原子锁 [APUE]关于windows多线程编程的学习笔记

Windows多线程开发之并发线程程序研究

做为一名分布式服务器开发人员,在服务器开发领域.多线程开发和并发编程方面有自己的心得和经验,愿意分享给同仁,今讨论下Windows下线程并发程序开发. 下面用用两个线程实现一个简单的数组排序,演示了线程的基本用法. 原理是: 为了节省执行时间而添加并行,把问题划分为几个小问题,并分配给几个线程(分而治之),把问题划分成若干更小的单元,更容易在实现中创建并行逻辑.同时,在并行中使用系统资源能优化应用程序并提高其运行速度. #include "stdafx.h"  #include <