windows下使用Critical Section和Mutex实现线程同步实例
Posted sysu_zjl
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了windows下使用Critical Section和Mutex实现线程同步实例相关的知识,希望对你有一定的参考价值。
利用critical section 和 Mutex两种不同的线程同步的方法实现生产者消费者问题。生产者线程要能够对一个计数器进行增的操作,并且将其输出在控制台上,消费者线程能够对这个计数器进行减的操作,并将其输出在控制台上。两种线程都共享一个计数器。
其中增、减计数器的数我设置为1~6随机。
测试两种方法的对比,用网上整理出的一张表如下
1、使用CriticalSection 方法时,有一个临界区cs
在将临界区传递给 InitializeCriticalSection 时(或者更准确地说,是在传递其地址时),临界区即开始存在。
初始化之后,代码即将临界区传递给 EnterCriticalSection 和 LeaveCriticalSection API。一个线程自 EnterCriticalSection 中返回后,所有其他调用EnterCriticalSection 的线程都将被阻止,直到第一个线程调用 LeaveCriticalSection 为止。
int count = 0; //counter value
CRITICAL_SECTION cs;//临界区
int maxinum, mininum;//设置counter value最大值,最小值
int producerCallNum = 0, consumerCallNum = 0;//生产者、消费者分别进入临界区次数
2、使用mutex方法时,有一个互斥锁mutex
HANDLE Mutex = NULL;//互斥锁
创建一个互斥器:CreateMutex;
获得互斥器的拥有权:WaitForSingleObject、WaitForMultipleObjects 等一类等待的函数……,第一个线程调用所有其他调用WaitForSingleObject的线程都将被阻止,直到第一个线程ReleaseMutex为止
释放互斥器的拥有权:ReleaseMutex;
3、生产者线程不断交替地执行如下两个阶段:睡眠一段随机时间0~1000,向计数器增加一个随机值,其值位于1~6之间。
DWORD WINAPI producer(LPVOID Param) {
pthreadID * pID = (pthreadID *)Param;
while (true) {
//sleep for a random period of time
Sleep(rand()%1000);
srand(count);
WaitForSingleObject(Mutex, INFINITE);
// generate
int add_count = rand() % 6 + 1;
//判断是否超过最大值
if (count < maxinum) {
count += add_count;
if (count > maxinum) {
add_count -= count - maxinum;
count = maxinum;
}
printf("Producer%d : produced %d items\\n", pID->id, add_count);
}
else
printf("Producer%d : counter value is full, cancel producing...\\n", pID->id);
printf("items num is %d\\n", count);
ReleaseMutex(Mutex);
//生产者进入临界区,次数增加
producerCallNum++;
}
return 0;
}
消费者线程也可睡眠一段时间,在醒后,对计数器减去一个随机值1~6 之间。
DWORD WINAPI consumer(LPVOID Param) {
pthreadID * pID = (pthreadID *)Param;
while (true) {
//sleep for a random period of time
Sleep(rand()%1000);
WaitForSingleObject(Mutex, INFINITE);
// generate
srand(count);
int decrease_count = rand() % 6 + 1;
//判断是否超过最小值
if (count > mininum) {
count -= decrease_count;
if (count <= mininum) {
decrease_count -= mininum - count;
count = mininum;
}
printf("Consumer%d : consumed %d items\\n", pID->id, decrease_count);
}
else
printf("Consumer%d : counter value is less than mixinum, cancel consuming...\\n", pID->id);
printf("items num is %d\\n", count);
ReleaseMutex(Mutex);
//消费者进入临界区,次数增加
consumerCallNum++;
}
return 0;
}
使用critical_section方法
#include <cstdlib>
#include <windows.h>
#include <cstdio>
#include "ctime"
int count = 0; //counter value
CRITICAL_SECTION cs;//临界区
int maxinum, mininum;//设置counter value最大值,最小值
int producerCallNum = 0, consumerCallNum = 0;//生产者、消费者分别进入临界区次数
struct pthreadID {
int id;
};
DWORD WINAPI producer(LPVOID Param) {
pthreadID * pID = (pthreadID *)Param;
while (true) {
//sleep for a random period of time
Sleep(rand()%1000);
srand(count);
EnterCriticalSection(&cs);
// generate
int add_count = rand() % 6 + 1;
//判断是否超过最大值
if (count < maxinum) {
count += add_count;
if (count > maxinum) {
add_count -= count - maxinum;
count = maxinum;
}
printf("Producer%d : produced %d items\\n", pID->id, add_count);
}
else
printf("Producer%d : counter value is full, cancel producing...\\n", pID->id);
printf("items num is %d\\n", count);
LeaveCriticalSection(&cs);
//生产者进入临界区,次数增加
producerCallNum++;
}
return 0;
}
DWORD WINAPI consumer(LPVOID Param) {
pthreadID * pID = (pthreadID *)Param;
while (true) {
//sleep for a random period of time
Sleep(rand()%1000);
EnterCriticalSection(&cs);
// generate
srand(count);
int decrease_count = rand() % 6 + 1;
//判断是否超过最小值
if (count > mininum) {
count -= decrease_count;
if (count <= mininum) {
decrease_count -= mininum - count;
count = mininum;
}
printf("Consumer%d : consumed %d items\\n", pID->id, decrease_count);
}
else
printf("Consumer%d : counter value is less than mixinum, cancel consuming...\\n", pID->id);
printf("items num is %d\\n", count);
LeaveCriticalSection(&cs);
//消费者进入临界区,次数增加
consumerCallNum++;
}
return 0;
}
int main(int argc, char * argv[]) {
srand(count);
int ThreadP_size = 5, ThreadC_size = 5;
int sleeptime;
pthreadID * pid, *cid; //标记生产者和消费者
InitializeCriticalSection(&cs);
HANDLE *ThreadHandleP, *ThreadHandleC; //生产者和消费者线程
DWORD *ThreadIdP, *ThreadIdC; //存储生产者和消费者线程ID
printf("please input the number of ThreadProducer : \\n");
scanf("%d", &ThreadP_size);
printf("please input the number of ThreadConsumer : \\n");
scanf("%d", &ThreadC_size);
printf("please input the maxinum of counter values : \\n");
scanf("%d", &maxinum);
printf("please input the mininum of counter values : \\n");
scanf("%d", &mininum);
printf("please input the main function sleeptime(s) after create threads : \\n");
scanf("%d", &sleeptime);
printf("the data can see at 'CriticalSection_output.txt'\\n");
//动态分配
ThreadHandleP = (HANDLE *)malloc(ThreadP_size * sizeof(HANDLE));
ThreadHandleC = (HANDLE *)malloc(ThreadC_size * sizeof(HANDLE));
ThreadIdP = (DWORD *)malloc(ThreadP_size * sizeof(DWORD));
ThreadIdC = (DWORD *)malloc(ThreadC_size * sizeof(DWORD));
pid = (pthreadID*)malloc(ThreadP_size * sizeof(DWORD));
cid = (pthreadID*)malloc(ThreadC_size * sizeof(DWORD));
freopen("CriticalSection_output.txt", "w", stdout);
printf("the number of ThreadProducer : %d\\n", ThreadP_size);
printf("the number of ThreadConsumer : %d\\n", ThreadC_size);
printf("the maxinum of counter values : %d\\n", maxinum);
printf("the mininum of counter values : %d\\n", mininum);
printf("the main function sleeptime(s) after create threads : %d (s) \\n", sleeptime);
//create producer thread(s)
for (int i = 0; i < ThreadP_size; i++) {
pid[i].id = i + 1;
ThreadHandleP[i] = CreateThread(NULL, 0, producer, &pid[i], 0, &ThreadIdP[i]);
}
//create consumer thread(s)
for (int i = 0; i < ThreadC_size; i++)
{
cid[i].id = i + 1;
ThreadHandleC[i] = CreateThread(NULL, 0, consumer, &cid[i], 0, &ThreadIdC[i]);
}
//sleep
Sleep(sleeptime*1000);
//exit
printf("the producer produced %d times\\n", producerCallNum);
printf("the consumer consumed %d times\\n", consumerCallNum);
fclose(stdout);
return 0;
}
使用mutex方法实现
#include <cstdlib>
#include <windows.h>
#include <cstdio>
#include "ctime"
int count = 0; //counter value
HANDLE Mutex = NULL;//互斥锁
int maxinum, mininum;//设置counter value最大值,最小值
int producerCallNum = 0, consumerCallNum = 0;//生产者、消费者分别进入临界区次数
struct pthreadID {
int id;
};
DWORD WINAPI producer(LPVOID Param) {
pthreadID * pID = (pthreadID *)Param;
while (true) {
//sleep for a random period of time
Sleep(rand()%1000);
srand(count);
WaitForSingleObject(Mutex, INFINITE);
// generate
int add_count = rand() % 6 + 1;
//判断是否超过最大值
if (count < maxinum) {
count += add_count;
if (count > maxinum) {
add_count -= count - maxinum;
count = maxinum;
}
printf("Producer%d : produced %d items\\n", pID->id, add_count);
}
else
printf("Producer%d : counter value is full, cancel producing...\\n", pID->id);
printf("items num is %d\\n", count);
ReleaseMutex(Mutex);
//生产者进入临界区,次数增加
producerCallNum++;
}
return 0;
}
DWORD WINAPI consumer(LPVOID Param) {
pthreadID * pID = (pthreadID *)Param;
while (true) {
//sleep for a random period of time
Sleep(rand()%1000);
WaitForSingleObject(Mutex, INFINITE);
// generate
srand(count);
int decrease_count = rand() % 6 + 1;
//判断是否超过最小值
if (count > mininum) {
count -= decrease_count;
if (count <= mininum) {
decrease_count -= mininum - count;
count = mininum;
}
printf("Consumer%d : consumed %d items\\n", pID->id, decrease_count);
}
else
printf("Consumer%d : counter value is less than mixinum, cancel consuming...\\n", pID->id);
printf("items num is %d\\n", count);
ReleaseMutex(Mutex);
//消费者进入临界区,次数增加
consumerCallNum++;
}
return 0;
}
int main(int argc, char * argv[]) {
srand(count);
int ThreadP_size = 5, ThreadC_size = 5;
int sleeptime;
Mutex = CreateMutex(NULL, FALSE, NULL);
pthreadID * pid, *cid; //标记生产者和消费者
InitializeCriticalSection(&cs);
HANDLE *ThreadHandleP, *ThreadHandleC; //生产者和消费者线程
DWORD *ThreadIdP, *ThreadIdC; //存储生产者和消费者线程ID
printf("please input the number of ThreadProducer : \\n");
scanf("%d", &ThreadP_size);
printf("please input the number of ThreadConsumer : \\n");
scanf("%d", &ThreadC_size);
printf("please input the maxinum of counter values : \\n");
scanf("%d", &maxinum);
printf("please input the mininum of counter values : \\n");
scanf("%d", &mininum);
printf("please input the main function sleeptime(s) after create threads : \\n");
scanf("%d", &sleeptime);
printf("the data can see at 'Mutex_output.txt'\\n");
ThreadHandleP = (HANDLE *)malloc(ThreadP_size * sizeof(HANDLE));
ThreadHandleC = (HANDLE *)malloc(ThreadC_size * sizeof(HANDLE));
ThreadIdP = (DWORD *)malloc(ThreadP_size * sizeof(DWORD));
ThreadIdC = (DWORD *)malloc(ThreadC_size * sizeof(DWORD));
pid = (pthreadID*)malloc(ThreadP_size * sizeof(DWORD));
cid = (pthreadID*)malloc(ThreadC_size * sizeof(DWORD));
freopen("Mutex_output.txt", "w", stdout);
printf("the number of ThreadProducer : %d\\n", ThreadP_size);
printf("the number of ThreadConsumer : %d\\n", ThreadC_size);
printf("the maxinum of counter values : %d\\n", maxinum);
printf("the mininum of counter values : %d\\n", mininum);
printf("the main function sleeptime(s) after create threads : %d (s) \\n", sleeptime);
//create producer thread(s)
int i;
for (i = 0; i < ThreadP_size; i++) {
pid[i].id = i + 1;
ThreadHandleP[i] = CreateThread(NULL, 0, producer, &pid[i], 0, &ThreadIdP[i]);
}
//create consumer thread(s)
for (i = 0; i < ThreadC_size; i++)
{
cid[i].id = i + 1;
ThreadHandleC[i] = CreateThread(NULL, 0, consumer, &cid[i], 0, &ThreadIdC[i]);
}
//sleep
Sleep(sleeptime*1000);
//exit
printf("the producer produced %d times\\n", producerCallNum);
printf("the consumer consumed %d times\\n", consumerCallNum);
fclose(stdout);
return 0;
}
对比critical_section与mutex两种方法 主函数等待相同时间,分别看两种方法生产者,消费者进入临界区次数 第一次测试数据为
生产者线程数: 5
消费者线程数: 4
最大计数器值: 20
最小计数器值: 3
主函数等待时间: 4 (s)
第二次测试数据为
生产者线程数: 6
消费者线程数: 4
最大计数器值: 25
最小计数器值: 3
主函数等待时间: 1 (s)
第三次测试数据为
生产者线程数: 6
消费者线程数: 4
最大计数器值: 28
最小计数器值: 5
主函数等待时间: 10 (s)
第四次测试数据为
生产者线程数: 7
消费者线程数: 7
最大计数器值: 40
最小计数器值: 8
主函数等待时间: 30 (s)
消费者线程数: 4
最大计数器值: 30
最小计数器值: 10
主函数等待时间: 60 (s)
Pn代表生产者进入临界区次数,Cn代表消费者进入临界区次数
发现
critical_section
Mutex
两者性能差别不是很大,最后发现可能每次进入临界区前都会sleep一段时间导致远远大于EnterCriticalSection(&cs)与WaitForSingleObject (Mutex, INFINITE);的执行时间。
于是我就在进入临界区之前,生产者线程和消费者线程都执行5000次的EnterCriticalSection(&cs)与LeaveCriticalSection(&cs)
或WaitForSingleObject (Mutex, INFINITE)与ReleaseMutex(Mutex);
同样地对mutex的方法进行相应的操作
第一次测试数据为
生产者线程数: 5
消费者线程数: 4
最大计数器值: 20
最小计数器值: 3
主函数等待时间: 4 (s)
第二次测试数据为
生产者线程数: 6
消费者线程数: 4
最大计数器值: 25
最小计数器值: 3
主函数等待时间: 1 (s)
第三次测试数据为
生产者线程数: 6
消费者线程数: 4
最大计数器值: 28
最小计数器值: 5
主函数等待时间: 10 (s)
第四次测试数据为
生产者线程数: 7
消费者线程数: 7
最大计数器值: 40
最小计数器值: 8
主函数等待时间: 30 (s)
第五次测试数据为
生产者线程数: 7
消费者线程数: 4
最大计数器值: 30
最小计数器值: 10
主函数等待时间: 60 (s)
Pn代表生产者进入临界区次数,Cn代表消费者进入临界区次数
发现
很明显地,Mutex比critical_section方法进入临界区的次数较少,所以如 果为非跨进程的话,critical_section比Mutex占优些。
以上是关于windows下使用Critical Section和Mutex实现线程同步实例的主要内容,如果未能解决你的问题,请参考以下文章
windows下使用Critical Section和Mutex实现线程同步实例
windows下使用Critical Section和Mutex实现线程同步实例
windows下使用Critical Section和Mutex实现线程同步实例
win10电脑开机出现critical service failed
Kivy 错误,[CRITICAL] [Text] 无法找到任何有价值的文本提供程序(python 3.6.1)(windows 10)