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实现线程同步实例的主要内容,如果未能解决你的问题,请参考以下文章

0_Simple__simpleCallback

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)