多线程

本文介绍了Windows操作系统中多线程的基本概念,包括进程与线程的区别,以及线程的创建方法。线程的创建主要通过CreateThread函数实现。在多线程同时运行时,文章提到了互斥对象、事件对象和关键代码段三种同步机制,详细阐述了它们的工作原理和应用场景,并对比了它们的特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基本概念

进程

进程是指的打开的.exe程序,比如点击steam.exe,这就是打开了一个进程
当然你可以同时打开很多个steam
在steam上,我们也可以打开很多个附带的程序
所以,进程和程序是n对n的关系
进程由两部分别组成
操作系统用来管理进程的内核对象,是封装的,只有调用windows的函数才能访问
地址空间,这个很好理解
进程本身不执行任何东西,它仅仅知识一个存放线程的容器,真正执行的是线程

线程

线程也分为两部分,内核对象和线程栈
每个线程都会被分配一个时间片,只有在时间片的时间内,才会运行
多线程,顾名思义,即为多个线程运行,注意,在一个时间点,只会有一个线程在运行,我们之所以感觉是一起运行,是因为时间片太短

线程的创建

通过CreateThread函数,

HANDLE CreateThread(
  LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD
  DWORD dwStackSize,                        // initial stack size
  LPTHREAD_START_ROUTINE lpStartAddress,    // thread function
  LPVOID lpParameter,                       // thread argument
  DWORD dwCreationFlags,                    // creation option
  LPDWORD lpThreadId                        // thread identifier
);

一般来讲除了第三个参数LPTHREAD_START_ROUTINE是方法名,第四个是传入的参数外,其他几个不是null就是0.如

HANDLE h;
h = CreateThread(NULL,0,Fun1,(LPVOID)j,0,NULL);

Fun1就是入口函数名,(LPVOID)j,j就是要传入的数据,不过使用时一般要转换类型
实现线程的入口函数

DWORD WINAPI CNewTestDlg::Fun1(LPVOID lp1){
    //这里写具体实现代码
}

在MFC上自动添加该函数时要注意,该函数的返回是DWORD,WINAPI要建完后加入
LPVOID是返回的参数类型,lp1是变量名称,自己随便取,不过为了可读性,推荐以下取名lpParameter
然后建完线程需要关闭

CloseHandle(h);

以下是一个简单的线程实现代码

DWORD WINAPI Fun1Proc(LPVOID lpParameter);

void main()
{
    HANDLE h1;
    h1 = CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);
    CloseHandle(h1);
}

DWORD WINAPI Fun1Proc(LPVOID lpParameter){
            cout<<"hello"<<endl;
}

最后会输出hello

多线程同时运行

这里的同时运行并不是真的同一时间,而是交替运行,只是交替时间很短,人眼无法分别
一共有三种方法实现
互斥对象、事件对象、关键代码段

互斥对象

//创建互斥对象
HANDLE hMutex = CreateMutex(NULL,FALSE,NULL);
//释放
ReleaseMutex(hMutex );

例子

void main()
{
    HANDLE h1;
    HANDLE h2;

    HANDLE hMutex = CreateMutex(NULL,FALSE,NULL);

    h1 = CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);
    h2 = CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);
    CloseHandle(h1);
    CloseHandle(h2);

    ReleaseMutex(hMutex );
}

代码放置位置如上

事件对象

事件对象有两种类型:人工重置和自动重置
人工重置的事件对象得到通知后,等待该实践对象的所有线程均变为可调度线程
自动重置的事件对象得到通知后,等待该实践对象的线程只有一个变为可调度线程

HANDLE CreateEvent(  LPSECURITY_ATTRIBUTES lpEventAttributes, // SD
  BOOL bManualReset,                       // reset type
  BOOL bInitialState,                      // initial state
  LPCTSTR lpName                           // object name);

第一个是结构体指针,null时使用默认的安全性
第二个是为bool型,true时为人工重置的事件对象,false为自动重置的事件对象
第三个是bool型,true时为有信号,false无信号
第四个是事件对象的名称,null时,默认为匿名信件

SetEvent函数,将指定事件对象设置为有信号
ResetEvent函数,将指定事件对象设置为无信号
两个函数均为bool型

关键代码段

也称临界区,它是指小段代码,在代码能够执行前,它必须独占某些资源的访问权
就像公共厕所,使用前,必须判断里面有没有人,没人才能上厕所
实现依赖于InitializeCriticalSection(LPCRITICAL_SECTION 变量名)函数
该函数只有一个参数,指向CriticalSection的指针,通过这个来实现判断
但是这种方式可能出现线程死锁

三者的对比

互斥对象和事件对象都是内核对象,速度较慢,但是可以在多个进程中的各个线程间进行同步
关键代码段速度快,但是容易死锁

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值