1 并行概念
问题:怎么让1个应用程序中的2个“函数”看起来好像是同时在运行呢?
—— 线程;
问题:怎么让2个“应用程序”看起来好像是在同一个处理器中同时运行呢?
—— 进程;
1.1 虚拟地址空间
虚拟:指的是对于每个应用程序来说,它的地址空间自己看上去好像是无限宽广!
1.1.1 进程的概念
实际上,同一时间,只有1个CPU,只有1个寄存器组!
MMU的作用:将每个应用程序的“虚拟地址”映射到真正的“物理地址”上!通过MMU我们将1个处理器,编程多个“假的处理器”!
1.1.2 线程的概念
多线程的作用:让1个程序里面的多个函数一起跑起来!!!
各个线程以“轮转”的方式平等占用处理器一段时间(这个时间可能是100 ms)。
示例:售票员问题
问题:假设的售票大厅共有10个售票员,150张票,请模拟10个售票员同时售卖150张票的情形!
模拟代码(非并行):
void SellTickets(int agentID, int numTicketsToSell)
{
while(numTicketsToSell > 0) {
printf("Agent %d sells a ticket\r\n", agentID),
numTicketsToSell--;
}
printf("Agent %d: All Down\r\n", agentID);
}
int main()
{
uint8_t agent;
int numAgents = 10; // 10个售票员
int numTickets = 150; // 150 张票
for(agent = 0; agent < numAgents; agent++) {
SellTickets(agent, numTickets / numAgents);
}
return 0;
}
预计结果:160行printf输出!这些票是从第0位售票员开始,依次到第1位售票员、第2位售票员……第9位售票员,1张1张地卖出去的,并不是10个售票员同时售票!!!这样做,10个人弄得跟1个人一样!!!效率太低了!!!
我们肯定希望,10个售票员同时往出买票。
需求:需要寻找1个线程库,来帮助你实现“线程”!
2 windows下的多线程
多线程的意义在于,1个线程在被阻塞的时候,另1个线程还可以运行!你想想啊,最开始学单片机的时候,有个叫“软件延时的”,这是纯粹消耗机器周期以进行延时。在“软件延时”的过程中,CPU干不了别的事情,它只能在那里干等着!这不是浪费资源嘛!
2.1 C的 <process.h> 与 <windows.h>
<process.h>
序号 | 函数名 | 功能 |
---|---|---|
1 | _beginthread() | 创建1个新线程(简洁版) |
2 | _endthread() | 销毁1个线程(简洁版) |
3 | _beginthreadex() | 创建1个新线程(全面版) |
4 | _endthreadex() | 销毁1个线程(全面版) |
<windows.h>
序号 | 函数名 | 功能 |
---|---|---|
1 | ResumeThread() | 恢复线程的运行 |
2 | SuspendThread() | 挂起线程 |
3 | GetExiCodeThread() | 得到1个线程的退出码 |
4 | WaitForSingleObject() | 等待1个对象 |
5 | WaitForMultipleObjects() | 等待多个对象 |
2.1.1 创建线程的方法
<process.h> 中提供了2种创建线程的方式!1个简洁版本的 _beginthread(),这个获取不了子线程的ID号;另1个是全面版本的 _beginthreadex(),这个可以获取子线程的ID号!!!
2.1.1.1 _beginthread() 原型
_CRTIMP uintptr_t __cdecl _beginthread(void (__cdecl *_StartAddress) (void *),
unsigned int _StackSize,
void *_ArgList);
- 第1个参数 *_StartAddress
你要执行的函数入口地址(名字); - 第2个参数 _StackSize
分配给这个线程的栈大小,默认0(1MB); - 第3个参数 *_ArgList
传递给线程的参数列表,无参时写NULL; - 返回值
成功则返回新线程的句柄,失败则返回0
2.1.1.2 _beginthreadex() 原型
_CRTIMP uintptr_t __cdecl _beginthreadex(void *_Security,
unsigned int _StackSize,
unsigned int (__stdcall *_StartAddress) (void *),
void *_ArgList,
unsigned int _InitFlag,
unsigned int *_ThrdAddr);
- 第1个参数 *_Security
安全属性,默认NULL; - 第2个参数 _StackSize
分配给这个线程的栈大小,默认0(1MB); - 第3个参数 *_StartAddress
你要执行的函数地址(名字) - 第4个参数 *_ArgList
函数入参列表 - 第5个参数 _InitFlag
新线程的初始状态,0表示立即执行,CREATE_SUSPENDED 表示挂起 - 第6个参数 *_ThrdAddr
用来传出线程的ID号 - 返回值
成功则返回新线程的句柄,失败则返回0
示例:运行1个线程
例1:使用简洁的 _beginthread()
typedef struct tag_paramStruct {
</