Win32和c++11多线程
一、概念
进程要想执行任务,必须得有线程,线程是进程的基本执行单元,一个进程的所有任务都在线程中执行。
1.线程的特点
线程内核对象
线程控制块
线程是独立调度和分派的基本单位
共享进程的资源
2.线程的上下文切换
引起上下文切换的原因
3.线程的状态
二、Windows多线程API
头文件#include<Windows.h>
1.CreateThread创建线程
参数说明:
线程的句柄是一块地址,线程ID可以用GetCurrentThreadId()函数获得。
2.获取线程ID
3.关闭线程句柄
关闭句柄后线程还会继续执行。
4.挂起线程
5.恢复线程
6.休眠线程的执行
7.WaitForSingleObject
等待一个内核对象变为已通知状态。这个函数常用于线程同步,确保一个线程在继续执行之前等待某个事件(如线程结束、互斥体释放、信号量达到等)。
未通知状态:该句柄关联的线程未结束,仍在执行。
已通知状态:该句柄关联的线程执行结束。
参数:
hHandle
:等待的对象的句柄。这个句柄可以是各种同步对象,如事件、互斥体、信号量、进程或线程。dwMilliseconds
:超时时间,以毫秒为单位。如果设置为INFINITE
,表示无限等待,直到对象进入信号状态。
返回值:
WaitForSingleObject
返回一个 DWORD
值,表示函数的结果。常见的返回值包括:
WAIT_OBJECT_0
:指定的对象已进入信号状态。WAIT_TIMEOUT
:等待超时,指定的对象在超时时间内未进入信号状态。WAIT_FAILED
:函数调用失败。可以通过调用GetLastError
函数获取扩展错误信息。
8.终止线程
9.获取线程结束码
10.WaitForMultipleObjects
参数说明:
11._beginthread和_endthread
CreateThread不安全
_beginthread
参数说明:
返回值:
_endthread
三、多线程模拟火车站售票
1.介绍
2.实现
3.为什么会出现卖出了第0张票?
四、多线程之间的同步和互斥
1.临界区
临界区结构对象
初始化临界区
进入和离开临界区
如果这样加锁,那么只要有一个线程进入临界区,除非所有票卖完,否则不会释放临界区。
如果这样加锁,又会出现卖出第0张票的情况。
最后这种情况,修改代码,在进入临界区后再次判断,可以避免上述情况。
尝试进入临界区
区别
删除临界区
2.线程死锁
死锁产生的必要条件
3.信号量
临界区与信号量对比
临界区
用于保护共享资源的代码块,确保在同一时间只有一个线程能够执行该代码块。
通常用于同一进程内的线程同步。
信号量
是一种更通用的同步机制,允许多个线程同时访问一定数量的共享资源。
可以用于进程间同步(IPC)。
相关API
(1)创建信号量
(2)P操作
(3)V操作
实现进程或线程只有一个实例
虽然每个进程有自己的地址空间,但命名对象(如命名信号量、命名互斥体等)是在系统范围内共享的。这意味着即使进程有各自的地址空间,命名对象在创建时会注册在操作系统的命名空间中,其他进程可以通过同样的名字访问这些对象。
在 Windows 操作系统中,命名对象(包括信号量、互斥体、事件等)在系统命名空间中共享。也就是说,当一个进程创建一个命名信号量时,操作系统会将该信号量注册在全局命名空间中。其他进程如果尝试创建或打开同名的信号量,就可以访问到这个信号量。
4.互斥量mutex
相关API
(1)创建互斥量
bInitialOwner:指定调用线程是否在互斥对象的初始状态下获得所有权。如果这个值为 TRUE
,调用线程在互斥对象创建成功后立即获得所有权;否则,互斥对象的初始状态为非信号状态。
(2)获得互斥量
(3)释放互斥量
示例
利用互斥量实现进程只有一个实例
5.事件Event
有信号状态和无信号状态
在 Windows 操作系统中,事件对象用于线程同步,其状态可以是“有信号”(signaled)或“无信号”(nonsignaled)。这两种状态用于控制线程的执行,具体如下:
- 当事件对象处于有信号状态时,所有等待该事件的线程都将被解除阻塞,并继续执行。这意味着事件发生了,等待的线程可以继续进行它们的工作。
- 当事件对象处于无信号状态时,所有等待该事件的线程都将被阻塞,直到事件对象的状态变为有信号。这意味着事件尚未发生,等待的线程需要等待,直到事件发生。
事件对象可以分为两种类型:自动重置事件(auto-reset event)和手动重置事件(manual-reset event)。这两种类型的事件对象在状态变更和重置机制上有所不同。
- 当事件对象处于有信号状态时,等待的线程将被解除阻塞,然后事件对象自动重置为无信号状态。如果有多个线程在等待事件,只有一个线程会被解除阻塞。
- 当事件对象处于有信号状态时,所有等待的线程将被解除阻塞,并且事件对象保持有信号状态,直到显式调用
ResetEvent
函数将其状态重置为无信号状态。
相关API
(1)创建事件
(2)把指定的事件对象设置为有信号状态
(3)把指定的事件对象设置为无信号状态
(4)等待事件对象的句柄
自动重置事件
手动重置事件
实现进程只有一个实例
6.PV操作
生产者消费者问题
7.总结
五、线程本地存储
1.静态TLS
2.动态TLS
六、多线程间的消息通讯
示例:一个线程向另一个线程发送消息
使用PeekMessage写法如下: