- 公开视频 -> 链接点击跳转公开课程
- 博客首页 -> 链接点击跳转博客主页
目录
线程优先级和调度
生产者与消费者模式
关键概念
实现细节
案例描述(一对一)
案例描述(多对一)
线程优先级和调度
- 在Windows操作系统中,线程优先级与进程优先级共同决定了线程的最终优先级。
-
进程优先级类决定了进程内所有线程的基本优先级,并且每个线程可以在此基础上有自己的优先级设置。
-
Windows提供了几个不同的进程优先级类:
-
IDLE_PRIORITY_CLASS
-
BELOW_NORMAL_PRIORITY_CLASS
-
NORMAL_PRIORITY_CLASS
-
ABOVE_NORMAL_PRIORITY_CLASS
-
HIGH_PRIORITY_CLASS
-
REALTIME_PRIORITY_CLASS
-
-
在每个进程优先级类内,线程可以有以下优先级:
-
THREAD_PRIORITY_LOWEST
-
THREAD_PRIORITY_BELOW_NORMAL
-
THREAD_PRIORITY_NORMAL
-
THREAD_PRIORITY_ABOVE_NORMAL
-
THREAD_PRIORITY_HIGHEST
-
THREAD_PRIORITY_TIME_CRITICAL
-
-
线程调度与时间碎片
-
Windows使用抢占式调度来分配CPU时间给线程。每个线程都会被分配一个时间片,它是线程可以在被挂起之前连续运行的时间量。
-
Windows中的实时优先级类别允许线程以最小的延迟执行。这些线程几乎总是优先于其他线程运行,除非有其他更高优先级的实时线程。这对于需要精确计时或快速响应的任务至关重要。
-
#include <iostream>
#include <Windows.h>DWORD WINAPI WorkThread(LPVOID lp)
{ std::cout << GetThreadPriority(GetCurrentThread());return 0;
}int main()
{// 设置进程优先级SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);// 获取进程优先级GetPriorityClass(GetCurrentProcess());// 创建执行线程HANDLE hThread = CreateThread(NULL, 0, WorkThread, NULL, 0, NULL);SetThreadPriority(hThread, THREAD_PRIORITY_HIGHEST);WaitForSingleObject(hThread, INFINITE);return 0;
}
生产者与消费者模式
关键概念
-
生产者(Producer) - 负责生成数据并将其放入缓冲区的线程或进程。通常会在没有足够空间放入新数据时等待。
-
消费者(Consumer) - 负责从缓冲区取出数据并处理它的线程或进程。如果缓冲区为空,消费者将等待直到有数据可用。
-
缓冲区(Buffer) - 一个有限的存储空间,用于生产者存放数据,消费者从中取出数据。它可以是一个队列、数组或任何其他形式的集合。
-
同步机制 - 生产者和消费者需要通过某种方式来协调它们的工作,以确保它们不会在同一时间内对缓冲区进行写入或读取操作。常见的同步机制包括互斥锁(mutexes)、信号量(semaphores)和条件变量(condition variables)。
实现细节
-
互斥锁 - 确保在任意时刻只有一个线程可以访问缓冲区。这防止了并发访问导致的数据竞争条件。
-
信号量 - 可以用来表示缓冲区中可用资源的数量。通常有两个信号量:一个表示空位的数量(对生产者而言),另一个表示数据项的数量(对消费者而言)。
案例描述(一对一)
- 假设有一个工厂(生产者)和一个零售商店(消费者)。工厂生产产品并将其放入仓库(缓冲区),而零售商店从仓库中取出产品并卖给客户。仓库的空间是有限的,所以当它满了时,工厂必须停止生产,等待零售商店取走一些产品。同样,如果仓库是空的,零售商店必须等待工厂生产新的产品。
-
初始化:设置一个有限大小的缓冲区以及必要的同步机制。
-
生产者线程执行以下操作:
-
确认缓冲区中是否有空间可用。
-
如果缓冲区已满,则等待直到有空位。
-
生产一个数据项并将其放入缓冲区中。
-
通知消费者缓冲区中有新的数据项可用。
-
-
消费者线程执行以下操作:
-
确认缓冲区内是否有数据项。
-
如果缓冲区为空,则等待直到有数据项。
-
从缓冲区中取出一个数据项进行处理。
-
通知生产者已从缓冲区中取出数据项,使其可以添加新的数据项。
-
-
同步机制:
-
使用互斥锁(mutex)保护对缓冲区的访问,以保证任一时刻只有一个线程可以操作缓冲区。
-
使用条件变量(condition variable)使线程在特定条件不满足时等待,并在条件满足时接到通知以继续执行。
-
-
终止过程:
-
生产者在完成既定数量的生产后终止。
-
消费者在消费完所有生产者生产的产品后终止。
-
#include <iostream>
#include <Windows.h>// 缓冲容量
#define BUFFER_SIZE 5
int Buffer[BUFFER_SIZE] = { 0 };// 交接信息
int In = 0;
int Out = 0;
int Sum = 0;
int Count = 0;// 同步对象
HANDLE hMutex = NULL;
HANDLE hProduce = NULL;
HANDLE hConsume = NULL;// 生产者
DWORD WINAPI Produce(LPVOID lp)
{for (size_t i = 0; i < Sum; i++){// 等待是否可以生成的信号WaitForSingleObject(hProduce, INFINITE);// 获取互斥体所有权WaitForSingleObject(hMutex, INFINITE);// 生成商品放入仓库Buffer[In] = i;In = (In + 1) % BUFFER_SIZE;++Count;std::cout << "Produce -> " << i << std::endl;// 释放互斥体所有权ReleaseMutex(hMutex);if (Count == 1){SetEvent(hConsume);}else if (Count < BUFFER_SIZE){SetEvent(hProduce);}else{ResetEvent(hProduce);}Sleep(rand() % 100);}return 0;
}// 消费者
DWORD WINAPI Consume(LPVOID lp)
{int nConut = 1;while (1){// 等待是否可以消费的信号WaitForSingleObject(hConsume, INFINITE);// 获取互斥体所有权WaitForSingleObject(hMutex, INFINITE);// 消费商品取出仓库int Data = Buffer[Out];Out = (Out + 1) % BUFFER_SIZE;--Count;std::cout << "Consume -> " << Data << std::endl;// 释放互斥体所有权ReleaseMutex(hMutex);if (Count == BUFFER_SIZE - 1){SetEvent(hProduce);}if (Count > 0){SetEvent(hConsume);}else{ResetEvent(hConsume);}Sleep(rand() % 100);// 条件满足 结束线程if (nConut == Sum) break;}return 0;
}int main()
{Sum = 20;// 初始同步对象hMutex = CreateMutex(NULL, FALSE, NULL);hProduce = CreateEvent(NULL, TRUE, TRUE, NULL);hConsume = CreateEvent(NULL, TRUE, FALSE, NULL);// 生产消费线程HANDLE thProduce = CreateThread(NULL, 0, Produce, NULL, 0, NULL);HANDLE thConsume = CreateThread(NULL, 0, Consume, NULL, 0, NULL);// 等待线程完成WaitForSingleObject(thProduce, INFINITE);WaitForSingleObject(thConsume, INFINITE);// 关闭对象句柄CloseHandle(hMutex);CloseHandle(hProduce);CloseHandle(hConsume);CloseHandle(thProduce);CloseHandle(thConsume);return 0;
}
案例描述(多对一)
- 假设有一个工厂,拥有多条生产线(生产者线程),每条生产线负责生产不同类型的产品。这些产品随后会被送往一个共享的仓库(缓冲区),以便单个零售商(消费者线程)从中取走产品并销售。由于仓库的容量有限,当仓库满时,所有生产线必须暂停生产,直到仓库中有足够的空间再继续生产。同样地,如果仓库为空,零售商也必须等待,直到仓库中有产品可供取出。
-
关键步骤
-
初始化:设置一个有限大小的缓冲区以及必要的互斥锁和条件变量(或事件)。
-
生产者线程执行以下操作:
-
确认缓冲区中是否有空间可用。
-
如果缓冲区已满,则等待直到有空位(通过条件变量或事件)。
-
获取互斥锁,生产一个数据项并将其放入缓冲区中,然后释放互斥锁。
-
通知消费者缓冲区中有新的数据项可用。
-
-
消费者线程执行以下操作:
-
确认缓冲区内是否有数据项。
-
如果缓冲区为空,则等待直到有数据项(通过条件变量或事件)。
-
获取互斥锁,从缓冲区中取出一个数据项进行处理,然后释放互斥锁。
-
通知生产者已从缓冲区中取出数据项,使其可以添加新的数据项。
-
-
实现注意事项
-
死锁避免:确保获取和释放锁的顺序一致,避免出现死锁情况。
-
条件竞争处理:使用互斥锁保护所有访问共享资源(例如,缓冲区和计数器)的操作,确保每次只有一个线程可以执行这些操作。
-
适当的信号通知:在修改了可能影响其他线程行为的共享状态后(例如,添加或移除缓冲区中的数据项),及时通过条件变量或事件发送适当的信号,以唤醒等待的线程。
-
#include <iostream>
#include <queue>
#include <Windows.h>// 共享数据
HANDLE hThreads[3] = { NULL };
std::queue<int> Queue;
CONST int Max = 10;// 线程竞态
CRITICAL_SECTION QueueLock = { 0 };
HANDLE hQueueNotEmpty = NULL;
HANDLE hQueueNotFull = NULL;
BOOL bProduceFinished = FALSE;// 生产者线程
DWORD WINAPI Produce(LPVOID lp)
{for (size_t i = 0; i < 50; i++){WaitForSingleObject(hQueueNotFull, INFINITE);EnterCriticalSection(&QueueLock);if (Queue.size() >= Max){ResetEvent(hQueueNotFull);}else{Queue.push(i);std::cout << "ID -> " << GetCurrentThreadId() << " Produce -> " << i << std::endl;SetEvent(hQueueNotEmpty);}LeaveCriticalSection(&QueueLock);Sleep(rand() % 100);}EnterCriticalSection(&QueueLock);bProduceFinished = TRUE;LeaveCriticalSection(&QueueLock);SetEvent(hQueueNotEmpty);return 0;
}// 消费者线程
DWORD WINAPI Consume(LPVOID lp)
{while (true){WaitForSingleObject(hQueueNotEmpty, INFINITE);EnterCriticalSection(&QueueLock);while (!Queue.empty()){int Data = Queue.front();Queue.pop();std::cout << "ID -> " << GetCurrentThreadId() << " Consume -> " << Data << std::endl;}if (bProduceFinished && Queue.empty()){LeaveCriticalSection(&QueueLock);break;}SetEvent(hQueueNotFull);LeaveCriticalSection(&QueueLock);Sleep(rand() % 100);}return 0;
}int main()
{// 初始同步对象InitializeCriticalSection(&QueueLock);hQueueNotFull = CreateEvent(NULL, TRUE, TRUE, NULL);hQueueNotEmpty = CreateEvent(NULL, FALSE, FALSE, NULL);// 启动执行线程hThreads[0] = CreateThread(NULL, 0, Produce, NULL, 0, NULL);hThreads[1] = CreateThread(NULL, 0, Produce, NULL, 0, NULL);hThreads[2] = CreateThread(NULL, 0, Consume, NULL, 0, NULL);// 等待线程结束WaitForMultipleObjects(3, hThreads, TRUE, INFINITE);// 释放对象资源DeleteCriticalSection(&QueueLock);for (auto i : hThreads) { CloseHandle(i); }CloseHandle(hQueueNotFull);CloseHandle(hQueueNotEmpty);return 0;
}