zoukankan      html  css  js  c++  java
  • 20160227.CCPP体系具体解释(0037天)

    程序片段(01):01.一对一模式.c+02.中介者模式.c+03.广播模式.c
    内容概要:事件

    ///01.一对一模式.c
    #include <stdio.h>
    #include <stdlib.h>
    #include <Windows.h>
    
    //01.关于多线程:
    //  (临界区+相互排斥量):线程冲突
    //  (事件):线程通信
    //  (时间):同步线程
    HANDLE eventArrA[2] = { 0 };
    HANDLE threadArrA[2] = { 0 };
    
    DWORD WINAPI haiHua(void * p)
    {
        printf("海华第01次说:i love you fangFang, please help me debug! 
    ");//信息通信内容
        Sleep(1000);//信息传递时间
        SetEvent(eventArrA[0]);//提示信息传到
        int i = 1;
        while (++i)
        {
            WaitForSingleObject(eventArrA[1], INFINITE);//等待信息传到
            printf("海华第%02d次说:i love you fangFang, please help me debug! 
    ", i);
            Sleep(1000);
            //ResetEvent(eventArrA[1]);//重置信息提示(手动)
            SetEvent(eventArrA[0]);
        }
    
        return 0;
    }
    
    DWORD WINAPI fangFang(void * p)
    {
        int i = 0;
        while (++i)
        {
            WaitForSingleObject(eventArrA[0], INFINITE);
            printf("王芳第%02d次说:sorry! but i love you! 
    ", i);
            Sleep(1000);
            SetEvent(eventArrA[1]);
        }
        return 0;
    }
    
    //02.关于CreateEvent(arg1, arg2, arg3, arg4);
    //  arg1:安全属性集---->通经常使用NULL
    //  arg2:手动重置事件-->手动:TRUE|自己主动:FALSE
    //      注:使用一次事件通知,用TRUE,使用多次事件通知,用FALSE
    //      注:使用一次线程通信,通经常使用的是信号量机制,而不是事件机制
    //  arg3:事件激活状态-->通经常使用FALSE
    //  arg4:事件唯一名称-->自己定义(便于检索指定事件)
    int main01(void)
    {
        eventArrA[0] = CreateEvent(NULL, FALSE, FALSE, NULL);
        eventArrA[1] = CreateEvent(NULL, FALSE, FALSE, NULL);
        threadArrA[0] = CreateThread(NULL, 0, haiHua, NULL, 0, NULL);
        threadArrA[1] = CreateThread(NULL, 0, fangFang, NULL, 0, NULL);
        WaitForMultipleObjects(2, threadArrA, TRUE, INFINITE);//维持多线程异步并发运行的状态
    
        system("");
    }
    
    
    
    //01.事件深入:
    //  1.事件用于线程通信
    //  2.事件的额外细节:三个案例
    //      两方通话---->三方通话---->一对多模式
    //      (相亲)            (媒婆)中介者     (广播)广播模式
    //02.了解一些问题:
    //  (临界区+相互排斥+原子变量):线程冲突
    //  事件:线程通信
    //  时间:同步线程
    //03.什么是死锁?
    //  编写事件的时候最easy遇到死锁的事情!
    //04.如今我们须要几个信号量,并且这个信号量我们用什么来进行描写叙述?
    //  时间通知+信号量
    //05.顺序不严密:
    //  1.等待信号之后,信号须要复原才行,否则会出现不正常的情况(信号错乱!)
    //  2.信号不同步和乱序的解决方式-收到信号之后进行复位
    //      (1).信号复位情况--必须复位,某些情况之下自己主动复位,建议主动复位
    //      (2).环绕信号:的False&TRUE的分别
    //          第二个參数:密切相关-自己主动&手动[复位情况]
    ///02.中介者模式.c
    #define _CRT_SECURE_NO_WARNINGS
    #include <stdio.h>
    #include <stdlib.h>
    #include <Windows.h>
    
    HANDLE threadArrB[3] = { 0 };
    HANDLE eventArrB[4] = { 0 };
    
    char strB[256] = { 0 };//线程通信内容
    
    //01.三方通话:中介者设计模式
    //  海华向中介者:发出事件通知0
    //      中介者等海华:等待事件通知0
    //  中介者向芳芳:发出事件通知1
    //      芳芳等中介者:等待事件通知1
    //  芳芳向中介者:发出事件通知2
    //      中介者等芳芳:等待事件通知2
    //  中介者向海华:发出事件通知3
    //      海华等中介者:等待事件通知3
    //  海华向中介者:发出事件通知0
    DWORD WINAPI haiHuaB(void * p)
    {
        sprintf(strB, "海华第01次说:i love you fangFang, please help me debug! 
    ");//发出通信内容
        Sleep(1000);//模拟通信时间
        SetEvent(eventArrB[0]);//提示通信到达
        int i = 1;
        while (++i)
        {
            WaitForSingleObject(eventArrB[3], INFINITE);
            memset(strB, '', 256);
            sprintf(strB, "海华第%02d次说:i love you fangFang, please help me debug! 
    ", i);
            Sleep(1000);
            SetEvent(eventArrB[0]);
        }
        return 0;
    }
    
    DWORD WINAPI ruiFuB(void * p)
    {
        int i = 0;
        int status = 0;//切换通信对象
        while (++i)
        {
            if (!status)
            {
                WaitForSingleObject(eventArrB[0], INFINITE);
                printf("媒婆传递海华通信内容(传递次数:%02d): 
    ", i);
                printf("	%s 
    ", strB);
                Sleep(1000);
                SetEvent(eventArrB[1]);
                status = 1;
            }
            else
            {
                WaitForSingleObject(eventArrB[2], INFINITE);
                printf("媒婆传递芳芳通信内容(传递次数:%02d): 
    ", i);
                printf("	%s 
    ", strB);
                Sleep(1000);
                SetEvent(eventArrB[3]);
                status = 0;
            }
        }
        return 0;
    }
    
    DWORD WINAPI fangFangB(void * p)
    {
        int i = 0;
        while (++i)
        {
            WaitForSingleObject(eventArrB[1], INFINITE);
            memset(strB, '', 256);
            sprintf(strB, "王芳第%02d次说:sorry! but i love you! 
    ", i);
            Sleep(1000);
            SetEvent(eventArrB[2]);
        }
        return 0;
    }
    
    int main02(void)
    {
        eventArrB[0] = CreateEvent(NULL, FALSE, FALSE, L"haiHua");
        eventArrB[1] = CreateEvent(NULL, FALSE, FALSE, L"ruiFuToFang");
        eventArrB[2] = CreateEvent(NULL, FALSE, FALSE, L"fangFang");
        eventArrB[3] = CreateEvent(NULL, FALSE, FALSE, L"ruiFuToHua");
        threadArrB[0] = CreateThread(NULL, 0, haiHuaB, NULL, 0, NULL);
        threadArrB[1] = CreateThread(NULL, 0, ruiFuB, NULL, 0, NULL);
        threadArrB[2] = CreateThread(NULL, 0, fangFangB, NULL, 0, NULL);
        WaitForMultipleObjects(3, threadArrB, TRUE, INFINITE);
    
        system("pause");
    }
    
    
    
    //01.中介者模式&广播模式&图论模式[多对多]
    //  中介者:三方
    //  广播:一对多
    //  图论:多对多
    //注:多线程这块儿必须要会树和图
    //02.一对多的情况下:
    //  既能够採用数组进行管理也能够採用链表进行管理
    //03.涉及到树的情况之下:
    //  QQ群聊天儿多线程,练就数据结构
    //04.一对多&多对多:
    //  群聊原理:中介者-->每一个人进行转发
    //  中介者进行转发原理-->数组管理-->数组广播-->群聊模式
    //05.流程梳理:
    //  1.我发送一条消息,中介者收到之后,他进行群发动作
    //  2.中介者的衍生模式-->形成闭环-->选好起始点
    //06.编程思想:精髓
    //  原则上一个消息全局变量读取特点
    // 相当于是一个公告栏,权限读取
    //07.操作:
    //      定义全局变量,便于读取全局变量的数据
    ///03.广播模式.c
    #define _CRT_SECURE_NO_WARNINGS
    #include <stdio.h>
    #include <stdlib.h>
    #include <Windows.h>
    
    HANDLE threadArrC[10] = { 0 };
    HANDLE eventArrC[1] = { 0 };
    
    char strC[256] = { 0 };//线程通信内容
    char chrArr[10] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K' };//代表十个人
    
    //01.一个线程发出事件通知消息,多条线程监听该事件通知消息
    //  一对多的模式
    DWORD WINAPI haiHuaC(void * p)
    {
        char * pChr = (char *)p;
        printf("i am %c haiHua's gir friend! 
    ", *pChr);
        if ('A' == *pChr)
        {
            MessageBox(0, TEXT("1"), TEXT("1"), 0);//暂停通知线程
            sprintf(strC, "海华女友%c speak:xiaohuahua lovely! 
    ", *pChr);//消息通知内容
            SetEvent(eventArrC[0]);//发出事件通知
            MessageBox(0, TEXT("1"), TEXT("1"), 0);//暂停通知线程
            sprintf(strC, "海华女友%c speak:xiaohuahua lovely! 
    ", *pChr);//消息通知内容
            SetEvent(eventArrC[0]);//发出事件通知
        }
        int i = 0;
        while (++i)
        {
            WaitForSingleObject(eventArrC[0], INFINITE);//等待事件通知
            printf("haiHua's girl friend %c read %s! 
    ", pChr, strC);
            Sleep(1000);
            ResetEvent(eventArrC[0]);
        }
    
        return 0;
    }
    
    int main03(void)
    {
        eventArrC[0] = CreateEvent(NULL, TRUE, FALSE, NULL);
        for (int i = 0; i < 10; ++i)
        {
            threadArrC[i] = CreateThread(NULL, 0, haiHuaC, &chrArr[i], 0, NULL);
        }
        WaitForMultipleObjects(10, threadArrC, TRUE, INFINITE);
    
        system("pause");
    }
    
    
    
    //01.中介者设计模式之广播模式:QQ群聊原理
    //  群聊-->数组-->链表-->环状-->局域网:环状结构[网络可靠性]
    //02.QQ的开发:不仅有发送和收取消息-->所以线程非常多
    //  信号对称-->须要进行手动进行事件的设置
    //      一对一:自己主动
    //      中介者:自己主动
    //      一对多:手动
    //      多对多:手动

    程序片段(02):01.Semaphore.c+02.SemaphoreNew.c
    内容概要:信号量

    ///01.Semaphore.c
    #include <stdio.h>
    #include <stdlib.h>
    #include <Windows.h>
    
    //01.信号量:
    //  1.相似于空位的特点:
    //  2.空置多少个位置就能够容纳多少个并行线程运行
    //注:当空余的位置为0的时候,也就不能在另起线程运行任务了
    #define id L"haiHua"//信号ID
    #define MAX 3//空位数
    
    //02.多线程信号量(semaphore)通信:
    //  1.特点:打开一个信号量(等同于检索一个指定ID名称的信号量)
    //  2.格式:HANDLE mySema = OpenSemaphore(arg1, arg2, arg3);
    //      arg1:信号量检索范围(SEMAPHORE_ALL_ACCESS)
    //      arg2:继承特性
    //      arg3:信号量检索名称(自己定义名称,在固定范围内唯一标识信号量)
    //  3.刚打开信号量的时候:
    //      信号量的空位为0,也就是无法开启新的异步线程运行任务
    //      信号量的空位为N,也就是说此刻能够开启N条异步线程运行任务代码
    //注:空位为N,表示除开当前线程之外能够另起的异步线程个数
    DWORD WINAPI myWorker(void * p)
    {
        int * pInt = (int *)p;
        printf("worker:%d si running! 
    ", *pInt);
        HANDLE mySema = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, id);//指定范围检索指定名称的信号量
        if (mySema)//推断信号量是否存在
        {
            printf("worker:%d is waiting! 
    ", *pInt);//表示当前线程处于判定信号量状态
            Sleep(1000);
            if (WaitForSingleObject(mySema, INFINITE) == WAIT_OBJECT_0)//等待空位为0
            {//假设信号空位为0,也就是无法开启异步线程的情况
                printf("worker:%d is getting! 
    ", *pInt);//此时仅仅有当前线程获得CPU运行权,其它线程无法获取CPU可运行权
                Sleep(3000);
                printf("worker:%d is leaving! 
    ", *pInt);
                ReleaseSemaphore(mySema, 1, NULL);//释放信号:仅仅是打开一个空位,也就是能够开启另外一条异步线程进行运行了
                CloseHandle(mySema);//释放资源信号量资源
            }
        }
        return 1;
    }
    
    //03.创建信号量:
    //  1.特点:初始化信号量对象
    //  2.格式:HANDLE mySema = CreateSemaphore(arg1, arg2, arg3, arg4);
    //      arg1:安全属性集
    //      arg2:初始空位数
    //      arg3:最大空位数
    //      arg4:信号量名称
    int main01(void)
    {
        HANDLE mySema = CreateSemaphore(NULL, 0, MAX, id);
        HANDLE threadArr[10] = { 0 };
        for (int i = 0; i < 10; ++i)
        {//由于当前信号量为0,因此多条运行同一段儿代码的时候,须要判定是否能通过
            threadArr[i] = CreateThread(NULL, 0, myWorker, threadArr + i, 0, NULL);
        }
        Sleep(5000);
        printf("激活状态! 
    ");
        ReleaseSemaphore(mySema, MAX, NULL);//释放信号量
        WaitForMultipleObjects(10, threadArr, TRUE, INFINITE);
        CloseHandle(mySema);
    
        system("pause");
    }
    
    
    
    //01.信号量:Semaphore
    //  1.量值:0-1-2-3
    //  2.使用一次进行一次减数,到了一定的数据之后,做一些指定操作
    //      当数据减到至0的时候,信号为0,在使用信号量的地方,处于停滞状态
    //  3.信号量还能够做一些其它的限定操作
    //  4.线程通信:用途
    //  5.具备等待机制
    //02.信号计数原理:
    //  1.两个按键入口,多个行李
    //  2.信号衰减原理:空位原理
    //      信号为0的时候,没有空位为0,通过推断信号的空位情况,决定是否让线程干活儿
    //      等待唤醒机制0与非0的差别(非0,线程可运行,0线程不可运行)
    //03.关卡原理:
    //  1.同一时候最多支持10000个人购票
    //  2.假设超过10000个人,就须要排队
    //  3.当10000个人购票完毕的时候,又一次开启线程运行任务
    //04.原理:if 0 等下去
    //  1.同一个信号量
    //  2.10个线程
    //  3.状态判定:
    //      0-->10个等待
    //      5-->5个等待,5个运行
    //          运行一次,减去一次-->信号量衰减
    //  4.全部的线程都能够读取到该信号量
    //      多个线程占用资源:等待运行状态
    //      用完与没实用完(线程不可运行状态与线程可运行状态)
    //05.信号量全然解析:
    //  步骤一:
    //      HANDLE hSEM=CreateSemaphore(NULL,0,MAX,id);
    //      创建一个信号量,信号量的最大值为MAX,假设等于0的情况之下,它就在这儿死等下去
    //  步骤二:
    //      ReleaseSemaphore(mySema,MAX,NULL);//释放信号量,补充空位数量
    //      一旦将信号量设定为5就会開始进行运行
    //06.什么样儿的情况之下我们使用信号量?
    //      实现两个线程的死锁状态,设定为1这个信号量,进或者不进
    ///02.SemaphoreNew.c
    #include <stdio.h>
    #include <stdlib.h>
    #include <Windows.h>
    
    int num = 0;
    
    //01.信号量的应用:
    //  1.排队运行机制
    //      让多个线程处于运行状态,让多个线程处于休眠状态
    //  2.实现线程相互排斥
    //      让一个线程处于运行状态,让其它全部线程处于等待状态
    DWORD WINAPI add(void * p)
    {
        HANDLE mySema = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, L"xiaobin");//打开信号量:
        if (mySema)
        {
            if (WAIT_OBJECT_0 == WaitForSingleObject(mySema, INFINITE))
            {//推断信号强弱(推断信号量的数目,也就是推断空位数目)
                for (int i = 0; i < 10000; ++i)
                {
                    ++num;
                }
                ReleaseSemaphore(mySema, 1, NULL);
                CloseHandle(mySema);
            }
        }
        else
        {
            printf("打开信号量失败! 
    ");
        }
    }
    
    int main02(void)
    {
        //01.实现线程相互排斥的关键代码:
        //  最多仅仅能有一个空位(最多仅仅能同一时候有一条线程处于运行状态)
        HANDLE mySema = CreateSemaphore(NULL, 0, 1, L"xiaobin");
        HANDLE threadArr[64] = { 0 };
        for (int i = 0; i < 64; ++i)
        {
            threadArr[i] = CreateThread(NULL, 0, add, NULL, 0, NULL);
        }
        printf("激活线程");
        ReleaseSemaphore(mySema, 1, NULL);
        WaitForMultipleObjects(64, threadArr, TRUE, INFINITE);
        printf("num = %d 
    ", num);
        CloseHandle(mySema);
    
        system("pause");
    }
    
    
    
    //01.信号量能够实现多个线程的卡顿现象
    //02.信号量的空位原理-->0和1的原理:相互排斥特点
    //  入职与离职原理的特点-->空位原理
    //03.怎样使用信号量实现一个全局变量的自增?
    //  相互排斥类型的方式实现-->使用信号量实现线程之间的相互排斥现象
    //04.随机数的求取方法:
    //  1.原始函数
    //  2.多线程的数据丢失
    //05.多线程处理状态下的CPU是不会稳定的情况
    //06.信号量:0代表没有空位-->线程等待状况
    //  控制訪问次数
    //07.相互排斥量与信号量的差别:
    //  相互排斥量:仅仅能让一个线程处于运行状态
    //  信号量:能够让多个线程处于运行状态,其它线程休眠
    //  临界区:仅仅能让一个线程处于运行状态
    //  时间同步:
    //  事件:也能够实现让多个线程处于运行状态,其它线程休眠状态
    //  原子操作:操作速度最快,由于它不须要等待操作线程,差点儿直接运行状态
    //注:原子量的速度快在于无需让多条线程处于等待运行状态,其它线程相互排斥的方式
    //      存在着线程等待运行的状态

    程序片段(03):01.相互排斥.c+02.相互排斥读写.c
    内容概要:相互排斥锁

    ///01.相互排斥.c
    #include <stdio.h>
    #include <stdlib.h>
    #include <Windows.h>
    
    int static num = 6400000;
    
    //01.全局写入锁
    //  当一个线程在进行指定变量的写入操作的时候;
    //  其它线程若是须要写入这个指定变量数据,那么
    //  其它线程仅仅能处于等待运行写入数据的状态
    SRWLOCK gw_lock = { 0 };
    
    static DWORD WINAPI write(void * p)
    {//多线程写入状态下,同一时刻仅仅能由一条线程运行写入状态!
        AcquireSRWLockExclusive(&gw_lock);//获得独立写入锁(进入锁定状态)
        for (int i = 0; i < 100000; ++i)
        {
            --num;
        }
        ReleaseSRWLockExclusive(&gw_lock);//释放独立写入锁(释放锁定状态)
        return 0;
    }
    
    int main01(void)
    {
        InitializeSRWLock(&gw_lock);
        HANDLE threadArr[64];
        for (int i = 0; i < 64; ++i)
        {
            threadArr[i] = CreateThread(NULL, 0, write, NULL, 0, NULL);
        }
        //num += 10000;//没有生效,说明相互排斥锁的原则是全局生效,是对全部线程生效!
        WaitForMultipleObjects(64, threadArr, TRUE, INFINITE);
        printf("num = %d 
    ", num);
    
        system("pause");
    }
    
    
    
    //01.相互排斥锁的概念:相互排斥
    //  如同交往一个女友之后就被锁定了
    //02.相互排斥锁问题:
    //  线程相互排斥:同一时刻,仅仅能由同一个线程运行数据操作
    //03.线程的相互排斥实现方式:
    //  临界区-->相互排斥量-->原子操作-->事件-->信号量-->相互排斥锁
    //04.相互排斥锁的创建流程:
    //  全局变量:定义相互排斥量
    //      SRWLOCK g_lock;
    //  Main函数:初始化相互排斥量
    //      InitializeSRWLock(&g_lock);
    //  数据锁定:写入和读取的锁定
    //  线程函数:
    //      AcquireSRWLockExclusive(&g_lock);//锁定写入
    //      ReleaseSRWLockExclusive(&g_lock);//锁定释放
    //05.相互排斥锁的使用场景:
    //  1.改变一个变量的时候须要锁定(防止同一时候读取,同一时候写入)
    //  2.读取一个变量的时候也须要锁定(防止同一时候写入,同一时候读取)
    ///02.相互排斥读写.c
    #include <stdio.h>
    #include <stdlib.h>
    #include <Windows.h>
    
    int num = 6400000;//待读写的数据
    
    SRWLOCK g_lock = { 0 };//写入锁
    
    DWORD WINAPI write(void * p)
    {
        printf("開始写入! 
    ");
        AcquireSRWLockExclusive(&g_lock);
        for (int i = 0; i < 100000; ++i)
        {
            --num;
            //Sleep(10);
        }
        ReleaseSRWLockExclusive(&g_lock);
        printf("结束写入! 
    ");
        return 0;
    }
    
    DWORD WINAPI read(void * p)
    {
        printf("读取状态! 
    ");
        AcquireSRWLockShared(&g_lock);
        int i = 0;
        while (1)
        {
            ++i;
            Sleep(1000);
            printf("第%d秒, num = %d 
    ", i, num);//由于写入状态锁定了,因此这里的读取状态,无法读取到数据
            if (20 == i)
                break;
        }
        ReleaseSRWLockShared(&g_lock);
        printf("读取结束! 
    ");
        return 0;
    }
    
    int main02(void)
    {
        InitializeSRWLock(&g_lock);
        CreateThread(NULL, 0, read, NULL, 0, NULL);
        HANDLE threadArr[64] = { 0 };
        for (int i = 0; i < 64; ++i)
        {
            threadArr[i] = CreateThread(NULL, 0, write, NULL, 0, NULL);
        }
        WaitForMultipleObjects(64, threadArr, TRUE, INFINITE);
        printf("num = %d 
    ", num);
    
        system("pause");
    }
    
    //01.相互排斥锁的读写状态控制
    //  写入的状态下不可读取,读取的状态下不可写入
    //02.锁定状态,读取完毕之后才进行锁定  
    //03.一个资源仅仅能锁定一次,不能锁定多次
    //04.锁定-->防止冲突问题-->读取和写入的状态
    //  防止同一时候写入和读取数据

    程序片段(04):Mutex.c
    内容概要:01.跨进程Mutex(发相互排斥)

    #include <stdio.h>
    #include <stdlib.h>
    #include <Windows.h>
    
    char name[100] = "haihualovefang";
    
    int main(void)
    {
        //01.创建相互排斥量"Mutex":
        //  位于内核层|Ring0层
        HANDLE myMutex = CreateMutexA(NULL, TRUE, name);
        printf("在内核层|Ring0层创建相互排斥量(Mutex)成功! 
    ");
        char chr = getch();//实时获取单个字符
        //02.释放相互排斥量:
        //  相当于发出通知
        ReleaseMutex(myMutex);
        //03.关闭相互排斥量:
        CloseHandle(myMutex);
        system("pause");
    }
    
    
    
    //01.关于跨进程的驱动訪问:内核对象
    //  不管是Windows还是Linux都是存在相互排斥量说法
    //02.假设是跨进程的话:
    //  创建跨进程的Mutext须要有名称(便于全局訪问)
    //03.编写网络程序的时候:
    //  既须要编写client也须要编写网络端
    //  -->编写两个程序的时代
    //04.演示的时候须要:
    //  进行编译好的程序之间的演示
    //05.跨进程通信:
    //  1.Event&Mutex&semaphore都能够实现跨进程的线程通信
    //  2.Mutex是最安全的跨进程线程訪问(由于能够处理发送通知方的断开情况)
    //      发出通信信息的进程退出情况能够处理!

    程序片段(05):Mutex.c
    内容概要:02.跨进程Mutex(收相互排斥)

    #include <stdio.h>
    #include <stdlib.h>
    #include <Windows.h>
    
    char name[100] = "haihualovefang";
    
    int main(void)
    {
        //01.打开相互排斥量:
        //  作用:获取相互排斥量
        //  格式:HANDLE mutex = OpenMutexA(arg1, arg2, arg3);
        //      mutex:相互排斥量+arg1:检索范围+arg2:继承句柄+相互排斥量名称
        HANDLE myMutex = OpenMutexA(MUTEX_ALL_ACCESS, TRUE, name);//获取相互排斥量
        //if (NULL == myMutex)//判定相互排斥量
        //{
        //  printf("获取相互排斥量失败! 
    ");
        //  system("pause");
        //  return;
        //}
        printf("等待状态! 
    ");
        //02.控制相互排斥量的等待时间:
        //  先获得相互排斥量-->设定等待状态时间(等待指定的时间范围!)
        DWORD res = WaitForSingleObject(myMutex, 10000);//设置等待状态
        switch (res)
        {
        case WAIT_OBJECT_0:
            printf("收到跨进程信号! 
    ");
            break;
        case WAIT_TIMEOUT:
            printf("等待跨进程信号超时! 
    ");
            break;
        case WAIT_ABANDONED:
            printf("另外一个进程发生终止!结束跨进程信号等待状态! 
    ");
            break;
        default:
            break;
        }
    
        CloseHandle(myMutex);
        system("pause");
    }
    
    //01.刚才的程序特点:
    //  都是出于同一个进程内的线程操作(同一进程)
    //02.C++关于"事件"和"信号量"的封装:
    //  封装通用的一个机制,Cocos2dx的时候都是一样的情况
    //  包括OC也一样,仅仅只是它们将接口内容进行了简化
    //03.多线程的强化:
    //  1.event&mutex&semaphore:(驱动层|Ring0层)
    //      本质:是处于驱动里面的一个综合信号量
    //  2.操作系统起到什么作用?
    //      (1).操作系统相似于一个巨大的进程,里面运行的每一个程序相似于线程
    //          (类比:大进程&进程)<--->(进程&线程)
    //      (2).电脑重新启动,打开多个.exe都须要重新启动
    //      (3).操作系统和应用程序之间的关系就如同进程和线程之间的关系
    //      (4).高级机制:内核对象(Ring0层对象)
    //04.操作系统的高级机制:内核对象-->项目使用-->跨进程使用
    //  1.操作系统的分层机制:
    //      (1).ring0:就是最底层,这里能够用于编写驱动(出错:蓝屏)
    //      (2).ring3:就是应用层,(出错:进程出错)
    //  2.线程相互排斥区分机制:
    //      (1).event&mutex:
    //          这里创建的指针处于应用层,可是指针所指向的内存处于ring0层
    //              ring0层其中的对象能够看到全部进程的内存(最高訪问权限)
    //      (2).进程之间不能够进行相互读写,必须通过注射方式
    //      (3).event&mutex都是出于ring0层的内核对象
    //          本质:对象的底层特点
    //          所以:它们不仅能够用于一个进程内的线程相互排斥,还能够用于多个进程之间的线程相互排斥
    //05.mutex的相互排斥问题解析:
    //  1.跨进程的mutex相互排斥问题
    //  2.C++的线程库都是对C语言多线程的封装
    //          大概原理-->C++的类使用
    //06.关于跨进程通信的问题:
    //  最好使用相互排斥量(mutex)实现跨进程通信
    //  原因:其它方式(event&semaphore)不能处理进程断开的情况!

    程序片段(06):发事件.c
    内容概要:01.跨进程Event(发事件)

    #include <stdio.h>
    #include <stdlib.h>
    #include <Windows.h>
    
    char name[100] = "haihualovefang";
    
    //01.Event实现跨进程通信:
    //  1.这儿的Event不是线程级别的含义,而是进程级别的含义:
    //      该Event实质上是位于(内核|Ring0层),因此能够实现跨进程通信
    //  2.參数说明:第二个參数表示是否重置手动重置事件状态
    //      TRUE:手动重置+FALSE:自己主动重置
    int main(void)
    {
        HANDLE myEvent = CreateEventA(NULL, FALSE, FALSE, name);//创建事件
        printf("跨进程Event创建成功! 
    ");
        char chr = getch();
        SetEvent(myEvent);//设置事件
        printf("发送跨进程Event事件! 
    ");
    
        CloseHandle(myEvent);
        system("pause");
    }
    
    
    
    //01.严格区分跨线程和跨进程
    //02.使用Event实现跨进程线程訪问
    //03.Event和Mutex有一定的差别:
    //  Event跨进程不能使用匿名的,否则的话找不到
    //注:跨进程一定要採用唯一名称标识信号
    //04.TCP/UDP的时候就是如此复杂的情况
    //05.一般进程与进程之间都须要设定一个超时等待时间
    //06.Event天生的缺陷:
    //  仅仅有Mutex能够感知到另外一个进程的丢失
    //  Event不具备感知进程丢失的功能
    //注:进程通信情况之下的进程丢失情况分析!

    程序片段(07):收事件.c
    内容概要:02.跨进程Event(收事件)

    #include <stdio.h>
    #include <stdlib.h>
    #include <Windows.h>
    
    char name[100] = "haihualovefang";
    
    int main(void)
    {
        //01.打开事件:
        //  获取跨进程所创建的事件
        HANDLE myEvent = OpenEventA(EVENT_ALL_ACCESS, TRUE, name);//获取事件
        if (NULL == myEvent)
        {
            printf("跨进程Event获取失败! 
    ");
            system("pause");
            return;
        }
        printf("跨进程Event等待状态! 
    ");
        DWORD res = WaitForSingleObject(myEvent, 10000);
        switch (res)
        {
        case WAIT_OBJECT_0:
            printf("跨进程Event收到状态! 
    ");
            break;
        case WAIT_TIMEOUT:
            printf("跨进程Event超时状态! 
    ");
            break;
        case WAIT_ABANDONED:
            printf("另外一个进程已经中止! 
    ");
            break;
        default:
            break;
        }
    
        CloseHandle(myEvent);
        system("pause");
    }

    程序片段(08):发信号.c
    内容概要:01.跨进程Semaphore(发信号)

    #include <stdio.h>
    #include <stdlib.h>
    #include <Windows.h>
    
    char name[100] = "haihualovefang";
    
    int main(void)
    {   
        HANDLE mySema = CreateSemaphoreA(NULL, 0, 1, name);
        printf("跨进程Semaphore信号量创建成功! 
    ");
        char chr = getch();
        ReleaseSemaphore(mySema, 1, NULL);
        printf("跨进程Semaphore发出信号! 
    ");
    
        CloseHandle(mySema);
        system("pause");
    }
    
    
    
    //01.当一条线程做完一件事情之后,须要通知其它线程的时候:
    //  这个时候就须要进行线程之间的通信
    //注:区分线程通信与进程通信
    //02.大数据你就得将图论和树结构玩儿的相当好才行
    //  图和树就是用于管理这么多的线程的
    //03.线程与线程之间的关系是非常复杂的:
    //  须要掌握逻辑&排序&容错&模糊
    //04.跨进程的线程通信:
    //  Event&Mutex&Semaphore
    //05.使用跨进程通信的时候:
    //  1.最佳解决方式就是Mutex
    //  2.缺点比較:
    //      Event&Semaphore:发信信号的进程关闭之后无法感知到!
    //      Mutex:发送信号的进程关闭之后能够被感知到!

    程序片段(09):收信号.c
    内容概要:02.跨进程Semaphore(收信号)

    #include <stdio.h>
    #include <stdlib.h>
    #include <Windows.h>
    
    char name[100] = "haihualovefang";
    
    int main(void)
    {
        HANDLE mySema = OpenSemaphoreA(SEMAPHORE_ALL_ACCESS, TRUE, name);
        if (NULL == mySema)
        {
            printf("跨进程Semaphore创建失败! 
    ");
            system("pause");
            return;
        }
        printf("跨进程Semaphore等待状态! 
    ");
        DWORD res = WaitForSingleObject(mySema, 10000);
        switch (res)
        {
        case WAIT_OBJECT_0:
            printf("跨进程Semaphore通信收到! 
    ");
            break;
        case WAIT_TIMEOUT:
            printf("跨进程Semaphore通信超时! 
    ");
            break;
        case WAIT_ABANDONED:
            printf("另外一个进程已经中止! 
    ");
            break;
        default:
            break;
        }
    
        CloseHandle(mySema);
        system("pause");
    }

    程序片段(10):TimePrc.c
    内容概要:时间同步

    #include <stdio.h>
    #include <stdlib.h>
    #include <Windows.h>
    
    //01.时间同步:标准回调函数格式
    //  1.小写"void"和大写"VOID"实质一样-->在这儿仅仅是回调函数的规范
    //  2.參数:普通指针+时间1[低点]+时间2[高点]-->相当于时差
    //  3.创建一个回调函数格式的函数指针常量
    //  4.回调函数:CALLBACK的标识定义(标准定义)
    VOID CALLBACK timeRun(void * pArg, DWORD timeLow, DWORD timeHigh)
    {
        DWORD dwindex = *(DWORD *)pArg;
        printf("第%d次! 
    ", dwindex);
        MessageBoxA(0, "1", "2", 0);
    }
    
    //02.Win操作系统之下使用系统自带的定时器资源:
    //  1.创建定时器:有几个函数-->起到等待作用的定时器
    //  2.參数:arg1,arg2,arg3-->arg3是定时器的名称
    //  3.匿名定时器仅仅能有一个,携带名称的定时器能够有多个!
    int main(void)
    {
        HANDLE time1 = CreateWaitableTimerA(NULL, TRUE, "haihua");
        if (NULL == time1)
        {
            printf("定时器创建失败! 
    ");
        }
        //设置定时器特点
        LARGE_INTEGER myTime;
        myTime.QuadPart = -50000000;//单位:0.1微妙--万分之中的一个毫秒
        //SetWaitTimer:定义解释
        //  _In_ HANDLE hTimer;定时器
        //  _In_ const LARGE_INTEGER * 1pDueTime;//时间
        //  _In_ LONG 1Period;//循环次数
        //  _In_opt_ PTIMERAPCROUTINE pfnCompletionRoutine;//函数指针
        //  _In_opt_ LPVOID 1pArgToCompletionRoutline;//參数                   
        //  _In_ BOOL fResume;//始终恢复状态
        //设置等待的定时器(等待定时器)
        DWORD dwparam = 1;
        //1000说明1000毫秒-->1分钟干一次,回调间隔
        if (SetWaitableTimer(time1, &myTime, 1000, timeRun, &dwparam, FALSE))
        {//五秒钟之后触发该事件:1|0
            printf("等待5秒之后開始干活儿! 
    ");
            for (int i = 0; i < 15; ++i, ++dwparam)
            {//运行次数-->循环多少次,就回调多少次
                SleepEx(INFINITE, TRUE);
            }
        }
        //循环完毕之后所需运行的操作:
        //  取消定时器和关闭句柄资源
        CancelWaitableTimer(time1);
        CloseHandle(time1);
    
        if (WAIT_OBJECT_0 == WaitForSingleObject(time1, INFINITE))
        {//等待消息成功
            printf("wait ok! 
    ");
        }
        else
        {
            printf("wait no! 
    ");
        }
    
        system("pause");
    }
    
    
    
    //01.多线程与队列:
    //  实现文件加密
    //02.关于"时间定时器"的一些操作:
    //  简单定时器-->同意回调函数
    //03.时间同步问题:
    //  1.主要用于解决多个线程的时间问题[多线程]
    //  2.环绕时间定时器,每隔一段事件干一定的活儿
    //  3.满足一定的时间条件,然后解决一定的问题
    //04.回调函数与时间的概念:
    //  1.触发函数的动作-->回调动作
    //  2.运行完一段代码之后,运行某一个函数
    //05.回调函数原理:
    //  1.数据1,2-->依据符号进行运算
    //  2.总体函数[參数1+參数2+函数指针]
    //  3.定时器触发一个函数的运行
    //  4.多个线程在同一时间要干某些事件
    //06.同一个事件通知多个事件的运行
    //07.回调函数:Callback
    //  1.函数指针,能够进行调用-->实现不论什么代码都能够进行自己定义
    //  2.整合功能:自己定义功能以及它定义功能
    //08.函数指针:新的功能-->函数指针-->功能随时更换
    //  1.百度搜索原理
    //  2.百度后台的搜索算法的改变
    //  3.用户调用的时候会依据函数指针的差别[付钱状态,区域]
    //09.创建多个定时器须要依据名称进行区分
    //10.定时器的使用步骤:
    //  1.创建定时器:
    //      HANDLE time1=CreateWaitableTimerA(NULL,TRUE,"haihua");
    //  2.五秒钟之后启动定时器:
    //      LARGE_INTEGER mytime;
    //      mytime.QuadPart=-50000000;
    //  3.定时器回调函数:
    //      if(SetWaitableTimer(time1,&mytime,3000,timerun,&dwparam,FALSE)){}
    //      回调周期:3000毫秒之后循环一次-->循环多少次
    //11.时间同步:主要用于游戏的开发
    //  内核对象:游戏开发-->为了时间而单独编写一条线程不划算
    //  CreateWaitableTimerA();-->内核对象
    //  SetWaitableTimer();-->内核对象
    //  内核对象-->多个时钟都有一个名称-->我就能够让多个线程同一时候读取一个时钟,进行操作,避免耗费资源

    程序片段(11):volatile.c
    内容概要:Volatile

    #include <stdio.h>
    #include <stdlib.h>
    #include <limits.h>
    
    void main01()
    {
        for (volatile int i = 0; i < INT_MAX; i++)
        {//(1)区分Debug模式和Release[+]模式
            //(2)优化:强制读取内存
        }
    
        system("pause");
    }
    
    void main02()
    {
        volatile int i = 10;
        int a = i;
        printf("
    i=%d", a);
    
        //{偷偷改变i的值}
        _asm
        {//(1)_asm是汇编语言
            //(2)改动数据-->栈底的内存是ebp,这里让其-4,也就是i这个栈底数据的改变
            //(3)16进制的20h=32
            //(4)这里的i已经不再寄存器里面,所以volatile强制读取内存其中经过改动之后的数据
            //(5)ebp-4相当于栈底的指针:直接改动数据
            mov dword ptr[ebp - 4], 20h;
        }
    
        int b = i;
        printf("
    i=%d", b);
    
        getchar();
    }
    
    
    
    //01.Volatile强化:
    //  主要解决强制读取内存的动作
    //02.Volatile原理:寄存器-内存
    //  1.寄存器读取i的值,然后赋值给a,b
    //  2.当检測到i没有被改动的时候,读取寄存器中的i值
    //  3.写入到a,b其中-->于是就缺少了读取内存的一次
    //  4.仅仅是读取了一次内存其中的i值
    //03.数据被意外改变的情况之下经常使用Volatile
    //  数据意外改变-->编译器优化-->不读取内存[失误]

    程序片段(12):Queue.h+Queue.c+数组顺序队列.c
    内容概要:01.数组顺序队列

    ///Queue.h
    #pragma once
    
    #define DT int
    #define EN 100
    
    typedef struct queue
    {
        int head;
        int tail;
        DT arr[EN];
    } Queue;
    
    void initQueue(Queue * pQueue);
    
    int queueIsFull(Queue * pQueue);
    
    void enQueue(Queue * pQueue, DT data);
    
    int queueIsEmpty(Queue * pQueue);  
    
    void showQueue(Queue * pQueue);
    
    DT queuGetHead(Queue * pQueue);
    
    void deQueue(Queue * pQueue);
    ///Queue.c
    #include "Queue.h"
    #include <stdlib.h>
    #include <memory.h>
    
    void initQueue(Queue * pQueue)
    {
        if (NULL == pQueue)
            abort();
        memset((*pQueue).arr, 0, EN * sizeof(DT));
        (*pQueue).tail = (*pQueue).head = 0;
    }
    
    int queueIsFull(Queue * pQueue)
    {
        if (NULL == pQueue)
            abort();
        if (EN == (*pQueue).tail)
            return 1;
        return 0;
    }
    
    void enQueue(Queue * pQueue, DT data)
    {
        if (NULL == pQueue)
            abort();
        if (queueIsFull(pQueue))
            return;
        (*pQueue).arr[(*pQueue).tail] = data;
        ++(*pQueue).tail;
    }
    
    int queueIsEmpty(Queue * pQueue)
    {
        if (NULL == pQueue)
            abort();
        if ((*pQueue).head == (*pQueue).tail)
            return 1;
        return 0;
    }
    
    void showQueue(Queue * pQueue)
    {
        if (NULL == pQueue)
            abort();
        if (queueIsEmpty(pQueue))
            return;
        for (int i = 0; i < (*pQueue).tail; ++i)
        {
            printf("%3d", (*pQueue).arr[i]);
        }
        printf("
    ");
    }
    
    DT queueGetHead(Queue * pQueue)
    {
        if (NULL == pQueue)
            abort();
        if (queueIsEmpty(pQueue))
            return;
        return (*pQueue).arr[0];
    }
    
    //01.几种特殊数据结构的实现方式:
    //  1.栈结构:
    //      数组栈:tail-=1(无所谓正向和反向)
    //      链表栈:
    //          正向:尾部添加,尾部降低
    //          反向:头部添加,头部降低
    //  2.队列结构:
    //      数组队列:正反向的效率一致
    //      链表队列:
    //          正向:尾部添加,头部降低
    //          反向:头部添加,尾部降低
    //注:数组队列,存在明显缺点,须要进行内存移动!
    //      队列的损耗,移动费时费力!
    //注:解决数组移动移动费时费力的方案:
    //      改造成环数组形队列+改造成链表队列
    void deQueue(Queue * pQueue)
    {
        if (NULL == pQueue)
            abort();
        if (queueIsEmpty(pQueue))
            return;
        for (int i = 0; i < (*pQueue).tail - 1; ++i)
        {
            (*pQueue).arr[i] = (*pQueue).arr[i + 1];
        }
        --(*pQueue).tail;
    }
    ///数组顺序队列.c
    #include "Queue.h"
    #include <stdio.h>
    #include <stdlib.h>
    
    int main01(void)
    {
        Queue queue;
        initQueue(&queue);
        for (int i = 0; i < 10; ++i)
        {
            enQueue(&queue, i);
        }
        showQueue(&queue);
        while (!queueIsEmpty(&queue))
        {
            printf("出队的数据是%3d 
    ", queueGetHead(&queue));
            deQueue(&queue);
            showQueue(&queue);
        }
    
        system("pause");
    }
    
    
    
    //01.顺序队列:逻辑编程
    //  工厂模式+(生产者-消费者)模式+请求响应模式
    //02.生产者与消费者:
    //  1.生产线程(生产者)
    //  2.消费线程(消费者)
    //  3.库存情况:库存越少越好,可是不能断掉供应链
    //      队列关系:生产者生产,消费者消费
    //      顺序关系:先进先出特点(存在顺序)
    //      原理:队列&多线程--请求|响应模式
    //03.三种队列的实现:
    //  1.数组顺序队列(尾部插入,头部取出)
    //  2.数组环形顺序队列(尾部插入,头部取出)
    //  2.链表反向队列(头部插入,尾部取出)
    //      队列实现:基础之上实现
    //          (生产者&消费者)模式
    //          (发送消息&接收消息)的模式
    //          (请求&响应)模式
    //04.数组顺序队列-->数组环形顺序队列
    //  单链表-->双链表:单独的结构-->追求快一点儿,从简
    //05.环形队列原理:
    //  1.吃东西-->拉东西
    //  2.吃:前面+,拉:往前走
    //  3.吃的太多,重合情况(特殊情况)
    //06.环形队列解释:
    //  1.头尾重合,没有数据,加入一个数据之后,头不变,尾向后移一个结构体单位
    //  2.顺序队列的缺点:删除的时候移动非常累(数组环形队列能够解决问题)
    //07.顺序队列解释:
    //  1.头部必须固定
    //  2.移动费时费力
    //08.队列移动问题的改造:
    //  1.链表结构
    //  2.环形队列

    程序片段(13):CircleQueue.h+CircleQueue.c+数组正向环形队列.c
    内容概要:02.数组环形顺序队列

    ///CircleQueue.h
    #pragma once
    
    #define DT int
    #define EN 0
    
    typedef struct circleQueue
    {
        int head;
        int tail;
        DT arr[EN];
    } CircleQueue;
    
    void initCircleQueue(CircleQueue * pCircleQueue);
    
    int circleQueueIsFull(CircleQueue * pCircleQueue);
    
    void enCircleQueue(CircleQueue * pCircleQueue, DT data);
    
    int circleQueueIsEmpty(CircleQueue * pCircleQueue);
    
    void showCircleQueue(CircleQueue * pCircleQueue);
    
    DT circleQueueGetHead(CircleQueue * pCircleQueue);
    
    void deCircleQueue(CircleQueue * pCircleQueue);
    ///CircleQueue.c
    #include "CircleQueue.h"
    #include <stdlib.h>
    #include <memory.h>
    
    void initCircleQueue(CircleQueue * pCircleQueue)
    {
        if (NULL == pCircleQueue)
            abort();
        memset((*pCircleQueue).arr, 0, EN * sizeof(DT));
        (*pCircleQueue).tail = (*pCircleQueue).head = 0;
    }
    
    //01.怎样推断环形队列是否装满元素?
    //  1.这儿有三种特殊情况须要考虑:
    //      头部+中部+尾部
    //  2.终于可归结为两种环形队列满元素的情况:
    //      头部+中部(尾部和头部合并)
    //  3.归纳总结:
    //      头尾+循环情况
    int circleQueueIsFull(CircleQueue * pCircleQueue)
    {
        if (NULL == pCircleQueue)
            abort();
        if ((*pCircleQueue).head == ((*pCircleQueue).tail + 1) % EN)
        {
            return 1;
        }
        return 0;
    }
    
    void enCircleQueue(CircleQueue * pCircleQueue, DT data)
    {
        if (NULL == pCircleQueue)
            abort();
        if (circleQueueIsFull(pCircleQueue))
            return;
        (*pCircleQueue).arr[(*pCircleQueue).tail] = data;
        (*pCircleQueue).tail = ((*pCircleQueue).tail + 1) % EN;
    }
    
    int circleQueueIsEmpty(CircleQueue * pCircleQueue)
    {
        if (NULL == pCircleQueue)
            abort();
        if ((*pCircleQueue).head == (*pCircleQueue).tail)
            return 1;
        return 0;
    }
    ///数组正向环形队列.c
    //01.数组顺序环形队列的思想:
    //  1.就是把数组当成闭合顺序环形队列(数组-->抽象-->环形队列)
    //  2.思想演示:
    //      (1).原型:1         2       3       4       5       6       7       8       9       10
    //                         rear                                        front
    //      (2).抽离:8         9       10  1       2
    //          原理:普通数组抽象为顺序环形队列
    //              1).front-->rear:两个指针轮询移动
    //              2).防止front和rear:都走到头的情况
    //              3).节约移动情况(环形队列的长处)
    //02.环形队列实现:
    //  数组法+链表法
    //  顺序法+逆序法
    //03.环形队列的应用场景:
    //  操作系统对线程的管理这块儿   
    //04.环形队列的两种情况:
    //  头尾情况+中部情况
    //05.环形队列:情况分析
    //  头尾+中部终于利用一个表达式进行表示        
    //06.环形链表:
    //  1.rear说明了元素的个数
    //      front=0&rear=5的情况
    //  2.rear重合情况二
    //  3.普通情况之下,要是想实现环形队列,数组或者链表都
    //      须要空出一个位置,防止front&rear重合
    //07.环形链表规则指定:
    //  1.为空:避免重合和满了的情况一致
    //  2.rear+1%5的特点-->代表存储继续前进
    //  3.满的情况综合:
    //      (rear+1)%5==front说明重合装满

    程序片段(15):CircleQueue.h+CircleQueue.h+数组正向环形队列.c
    内容概要:01.数组正向环形队列

    ///CircleQueue.h
    #pragma once 
    
    #define DT int
    #define EN 10
    
    //01.数组正向环形队列:
    //  长处:出队一个元素,无需进行队列数组元素的总体移动
    //  特点:假设模拟数组的长度为N
    //      普通队列:须要使用到N个元素
    //      环形队列:须要使用到N-1个元素
    //注:留出一个空位是为了区分队列重合情况和队列满载情况
    //  普通重合情况:就是空队列
    //  特殊重合情况:就是满队列
    typedef struct circleQueue
    {
        DT arr[EN];
        int head;
        int tail;
    }CircleQueue;
    
    void initCircleQueue(CircleQueue * pCircleQueue);
    
    int circleQueueIsFull(CircleQueue * pCircleQueue);
    
    void enCircleQueue(CircleQueue * pCircleQueue, DT data);
    
    int circleQueueIsEmpty(CircleQueue * pCircleQueue);
    
    void showCircleQueue(CircleQueue * pCircleQueue);
    
    DT circleQueueGetHead(CircleQueue * pCircleQueue);
    
    void deCircleQueue(CircleQueue * pCircleQueue);
    ///CircleQueue.c
    #include "CircleQueue.h"
    #include <stdlib.h>
    #include <memory.h>
    #include <stdio.h>
    
    void initCircleQueue(CircleQueue * pCircleQueue)
    {
        if (NULL == pCircleQueue)
            abort();
        memset((*pCircleQueue).arr, 0, EN * sizeof(DT));
        (*pCircleQueue).tail = (*pCircleQueue).head = 0;
    }
    
    int circleQueueIsFull(CircleQueue * pCircleQueue)
    {
        if (NULL == pCircleQueue)
            abort();
        if ((*pCircleQueue).head == ((*pCircleQueue).tail + 1) % EN)
            return 1;
        return 0;
    }
    
    //01.空位基本的作用:
    //  1.为了缓冲末尾位置能够进行循环填充数据!
    //  2.为了能够准确区分环形队列的两种情况:
    //      空队列+满队列
    //注:还能够确定最后一个入队的元素究竟应当放置于何处!
    void enCircleQueue(CircleQueue * pCircleQueue, DT data)
    {
        if (NULL == pCircleQueue)
            abort();
        if (circleQueueIsFull(pCircleQueue))
            return;
        (*pCircleQueue).arr[(*pCircleQueue).tail] = data;
        (*pCircleQueue).tail = ((*pCircleQueue).tail + 1) % EN;
    }
    
    int circleQueueIsEmpty(CircleQueue * pCircleQueue)
    {
        if (NULL == pCircleQueue)
            abort();
        if ((*pCircleQueue).head == (*pCircleQueue).tail)
            return 1;
        return 0;
    }
    
    void showCircleQueue(CircleQueue * pCircleQueue)
    {
        if (NULL == pCircleQueue)
            abort();
        if (circleQueueIsEmpty(pCircleQueue))
            return;
        //环形队列:元素不确定+起点不确定(无法确定循环次数)
        int i = (*pCircleQueue).head;
        int count = 0;
        do
        {
            printf("%3d", (*pCircleQueue).arr[(i++) % EN]);
            if (9 == ++count)
                break;
        } while ((((*pCircleQueue).tail + 1) % EN != i % EN) && (i %EN < (*pCircleQueue).tail));
        printf("
    ");
    }
    
    DT circleQueueGetHead(CircleQueue * pCircleQueue)
    {
        if (NULL == pCircleQueue)
            abort();
        if (circleQueueIsEmpty(pCircleQueue))
            return -1;
        return (*pCircleQueue).arr[(*pCircleQueue).head];
    }
    
    void deCircleQueue(CircleQueue * pCircleQueue)
    {
        if (NULL == pCircleQueue)
            abort();
        if (circleQueueIsEmpty(pCircleQueue))
            return;
        (*pCircleQueue).head = ((*pCircleQueue).head + 1) % EN;
    }
    ///数组正向环形队列.c
    #include "CircleQueue.h"
    #include <stdio.h>
    #include <stdlib.h>
    
    int main01(void)
    {
        CircleQueue circleQueue;
        initCircleQueue(&circleQueue);
        for (int i = 0; i < 9; ++i)
        {
            enCircleQueue(&circleQueue, i + 1);
            showCircleQueue(&circleQueue);
        }
        while (!circleQueueIsEmpty(&circleQueue))
        {
            printf("数组正向环形队列出队:%3d 
    ", circleQueueGetHead(&circleQueue));
            deCircleQueue(&circleQueue);
            showCircleQueue(&circleQueue);
        }
    
        system("pause");
    }

    程序片段(16):Queue.h+Queue.c+数组正向环形队列.c
    内容概要:02.数组正向环形队列(标准版)

    ///Queue.h
    #pragma once
    
    #define DT int
    #define EN 10
    
    //01.採用数组模拟队列的两种特点:
    //  1.假设待用于模拟的数组共同拥有N个元素
    //  2.两种目标队列模型:
    //      普通队列:数组正向队列,使用N个元素
    //      环形队列:数组正向环形队列,使用N-1个元素
    //注:环形队列,删除一个元素便无需移动
    typedef struct queue
    {
        DT arr[EN];
        int head;
        int tail;
    }Queue;
    
    void initQueue(Queue * pQueue);
    
    int queueIsFull(Queue * pQueue);
    
    void enQueue(Queue * pQueue, DT data);
    
    int queueIsEmpty(Queue * pQueue);
    
    void showQueue(Queue * pQueue);
    
    DT queueGetHead(Queue * pQueue);
    
    void deQueue(Queue * pQueue);
    ///Queue.c
    #include "Queue.h"
    #include <stdlib.h>
    #include <memory.h>
    #include <stdio.h>
    
    void initQueue(Queue * pQueue)
    {
        if (NULL == pQueue)
            abort();
        memset((*pQueue).arr, 0, EN * sizeof(DT));
        (*pQueue).tail = (*pQueue).head = 0;
    }
    
    //01.区分:数组正向环形队列的两种情况
    //  1.空队列:起始位置=终止位置
    //  2.满队列:起始位置=(终止位置+1)%EN;
    //注:关于环形队列的面试填空问题
    //  1.预留一个空数组元素用作这两种情况的区分
    //      空队列和满队列的准确区分
    //  2.使得环形队列的循环利用情况得到维持
    //      能够循环利用到环形队列其中的每一个元素位置
    //  3.极端情况分析:
    //      (1).头尾:head<tail
    //      (2).中间:head>tail
    //      (3).相同:head=tail
    int queueIsFull(Queue * pQueue)
    {
        if (NULL == pQueue)
            abort();
        if ((*pQueue).head == ((*pQueue).tail + 1) % EN)
        {
            return 1;
        }
        return 0;
    }
    
    //02.数组正向环形队列的入队比数组正向队列麻烦多了:
    //  1.特点就是:始终在模拟正向环形队列的数组其中空余一个元素位置
    //      用作区分空队列和满队列以及维持环形队列的循环状况
    //  2.走环形的特点!充分利用取余运算符的特点
    //注:取余运算符能够杜绝两种特殊情况:
    //      起点刚好冲数组首位置開始的情况
    //      起点不是位于数组首位置的情况
    //  特:在这两种情况之下都能够维持空余一个元素位置的特点
    //最后一个位置不管何种情况都不会被使用到!
    void enQueue(Queue * pQueue, DT data)
    {
        if (NULL == pQueue)
            abort();
        if (queueIsFull(pQueue))
            return;
        (*pQueue).arr[(*pQueue).tail] = data;
        (*pQueue).tail = ((*pQueue).tail + 1) % EN;//就是为了一定要空余最后一个位置
    }
    
    
    //03.空队列的两种情况:
    //  重合点为:(起点位置or终点位置)
    // 重合点为:模拟数组的不论什么位置!
    //注:实质上就是两点重合!
    int queueIsEmpty(Queue * pQueue)
    {
        if (NULL == pQueue)
            abort();
        if ((*pQueue).head == (*pQueue).tail)
            return 1;
        return 0;
    }
    
    void showQueue(Queue * pQueue)
    {
        if (NULL == pQueue)
            abort();
        for (int i = (*pQueue).head; i % EN < (*pQueue).tail ; ++i)
        {
            printf("%3d", (*pQueue).arr[i]);
        }
        printf("
    ");
    }
    
    DT queueGetHead(Queue * pQueue)
    {
        if (NULL == pQueue)
            abort();
        if (queueIsEmpty(pQueue))
            return;
        return (*pQueue).arr[(*pQueue).head];
    }
    
    void deQueue(Queue * pQueue)
    {
        if (NULL == pQueue)
            abort();
        if (queueIsEmpty(pQueue))
            return;
        (*pQueue).head = ((*pQueue).head + 1) % EN;
    }
    ///数组正向环形队列.c
    #include "Queue.h"
    #include <stdio.h>
    #include <stdlib.h>
    
    int main02(void)
    {
        //for (int i = 0;;++i)
        //{
        //  printf("%2d", i %10);
        //}
    
        Queue queue;
        initQueue(&queue);
        for (int i = 0; i < EN - 1; ++i)
        {
            enQueue(&queue, i + 1);
            showQueue(&queue);
        }
        while (!queueIsEmpty(&queue))
        {
            printf("%3d", queueGetHead(&queue));
            deQueue(&queue);
        }
    
        system("pause");
    }
    
    //01.环形队列:
    //  1.最后一个坑用于表示模拟结束:标识结束
    //      标识结束+区分空队列和满队列+可循环利用
    //  2.环形队列原理深究:
    //      环形队列的优先级问题-->顺序队列相同有
    //注:优先队列
    //02.环形队列的应用:
    //  1.高效应用
    //  2.操作系统在一段时间之内仅仅能运行一个线程
    //03.操作系统的特点:
    //  1.我一段时间限定内仅仅能运行一段儿程序,所以操作系统
    //      为每一条线程分配对应的时间片,然后获取时间片之后
    //      就開始运行-->操作系统1秒钟有1000次夺回控制权
    //  2.Windows属于抢占式操作系统
    //      操作系统时时刻刻夺回控制权,在又一次进行分配
    //  3.冻结状态与解冻状态的体现
    //04.处理队列的时候须要将数据更替为HANDLE类型
    //05.使用数组构建环形队列比使用链表构建环形队列简单多了
    //06.数组正向环形队列相比数组正向队列的长处:
    //      删除一个元素之后不须要进行移动,消耗效率

    程序片段(17):CircleQueue.h+CircleQueue.c+数组正向环形队列.c
    内容概要:01.数组正向环形队列

    ///CircleQueue.h
    #pragma once
    
    #define DT int
    #define EN 10
    
    //01.数组模拟队列:
    //  普通队列:使用N个数组元素
    //  环形队列:使用N-1个数组元素
    typedef struct circleQueue
    {
        DT arr[EN];
        int head;
        int tail;
    }CircleQueue;
    
    void initCircleQueue(CircleQueue * pCircleQueue);
    
    int circleQueueIsFull(CircleQueue * pCircleQueue);
    
    void enCircleQueue(CircleQueue * pCircleQueue, DT data);
    
    int circleQueueIsEmpty(CircleQueue * pCircleQueue);
    
    void showCircleQueue(CircleQueue * pCircleQueue);
    
    DT circleQueueGetHead(CircleQueue * pCircleQueue);
    
    void deCircleQueue(CircleQueue * pCircleQueue);
    ///CircleQueue.c
    #include "CircleQueue.h"
    #include <stdlib.h>
    #include <memory.h>
    #include <stdio.h>
    
    void initCircleQueue(CircleQueue * pCircleQueue)
    {
        if (NULL == pCircleQueue)
            abort();
        memset((*pCircleQueue).arr, 0, EN * sizeof(DT));
        (*pCircleQueue).tail = (*pCircleQueue).head = 0;
    }
    
    int circleQueueIsFull(CircleQueue * pCircleQueue)
    {
        if (NULL == pCircleQueue)
            abort();
        if ((*pCircleQueue).head == ((*pCircleQueue).tail + 1) % EN)//满队列
            return 1;
        return 0;
    }
    
    void enCircleQueue(CircleQueue * pCircleQueue, DT data)
    {
        if (NULL == pCircleQueue)
            abort();
        if (circleQueueIsFull(pCircleQueue))
            return;
        (*pCircleQueue).arr[(*pCircleQueue).tail] = data;//当前填充位置
        (*pCircleQueue).tail = ((*pCircleQueue).tail + 1) % EN;//下个填充位置+保证连续存储
    }
    
    int circleQueueIsEmpty(CircleQueue * pCircleQueue)
    {
        if (NULL == pCircleQueue)
            abort();
        if ((*pCircleQueue).head == (*pCircleQueue).tail)//空队列
            return 1;
        return 0;
    }
    
    void showCircleQueue(CircleQueue * pCircleQueue)
    {
        if (NULL == pCircleQueue)
            abort();
        if (circleQueueIsEmpty(pCircleQueue))
            return;
        for (int i = (*pCircleQueue).head; i%EN < (*pCircleQueue).tail; ++i)//i<=>i%EN:这里是环形队列没有出现特殊情况的特点!
        {//数组正向环形队列:1.不确定数组环形队列元素个数+2.不确定环形队列的起始元素和终止元素位置(因此打印无法控制)
            printf("%3d", (*pCircleQueue).arr[i]);
        }
        printf("
    ");
    }
    
    DT circleQueueGetHead(CircleQueue * pCircleQueue)
    {
        if (NULL == pCircleQueue)
            abort();
        if (circleQueueIsEmpty(pCircleQueue))
            return -1;
        return (*pCircleQueue).arr[(*pCircleQueue).head];
    }
    
    void deCircleQueue(CircleQueue * pCircleQueue)
    {
        if (NULL == pCircleQueue)
            abort();
        if (circleQueueIsEmpty(pCircleQueue))
            return;
        (*pCircleQueue).head = ((*pCircleQueue).head + 1) % EN;
    }
    ///数组正向环形队列.c
    #include "CircleQueue.h"
    #include <stdio.h>
    #include <stdlib.h>
    #include <Windows.h>
    
    int main01(void)
    {
        CircleQueue circleQueue = { 0 };
        initCircleQueue(&circleQueue);
        for (int i = 0; i < 9; ++i)
        {
            enCircleQueue(&circleQueue, i + 1);
            showCircleQueue(&circleQueue);
        }
        while (!circleQueueIsEmpty(&circleQueue))
        {
            printf("%3d 
    ", circleQueueGetHead(&circleQueue));
            deCircleQueue(&circleQueue);
            showCircleQueue(&circleQueue);
        }
    
        system("pause");
    }
    
    CircleQueue circleQueue = { 0 };
    
    DWORD WINAPI producer(void * p)
    {
        printf("生产者第01次运行生产任务! 
    ");
        int data = 0;
        while (!circleQueueIsFull(&circleQueue))
        {//生产者:一次性补充满库存黁量
            enCircleQueue(&circleQueue, ++data);
            printf("生产者生产了%3d! 
    ", data);
        }
        Sleep(1000);
        HANDLE event1 = OpenEvent(EVENT_ALL_ACCESS, TRUE, L"producer");
        SetEvent(event1);
    
        int i = 1;
        while (++i)
        {
            HANDLE event2 = OpenEvent(EVENT_ALL_ACCESS, TRUE, L"consumer");
            WaitForSingleObject(event2, INFINITE);
            printf("生产者第%02d次运行生产任务! 
    ", i);
            while (!circleQueueIsFull(&circleQueue))
            {
                enCircleQueue(&circleQueue, ++data);
                printf("生产者生产了%3d! 
    ", data);
            }
            Sleep(1000);
            SetEvent(event1);
        }
    
        return 0;
    }
    
    DWORD WINAPI consumer(void * p)
    {
        int i = 0;
        while (++i)
        {
            HANDLE event1 = OpenEvent(EVENT_ALL_ACCESS, TRUE, L"producer");
            WaitForSingleObject(event1, INFINITE);
            printf("消费者第%02d次运行消费任务! 
    ", i);
            int num = 9;
            for (int j = 0; j < num; ++j)
            {
                if (!circleQueueIsEmpty(&circleQueue))
                {
                    printf("消费者消费了%3d! 
    ", circleQueueGetHead(&circleQueue));
                    deCircleQueue(&circleQueue);
                }
            }
            Sleep(1000);
            HANDLE event2 = OpenEvent(EVENT_ALL_ACCESS, TRUE, L"consumer");
            SetEvent(event2);
        }
        return 0;
    }
    
    int main02(void)
    {
        HANDLE event1 = CreateEvent(NULL, FALSE, FALSE, L"producer");
        HANDLE event2 = CreateEvent(NULL, FALSE, FALSE, L"consumer");
    
        HANDLE threadArr[2] = { 0 };
        threadArr[0] = CreateThread(NULL, 0, producer, NULL, 0, NULL);
        threadArr[1] = CreateThread(NULL, 0, consumer, NULL, 0, NULL);
        //WaitForSingleObject(producer, INFINITE);//能够直接等待单个线程任务运行结束以后!
        WaitForMultipleObjects(2, threadArr, TRUE, INFINITE);
    
        CloseHandle(event1);
        system("pause");
    }
    
    
    
    //01.消费者不管买走多少,都须要将存储结构塞满
    //  紧缺产品:针对于畅销产品的库存解决方式
    //      随时保持库存充足
    //  停滞产品:针对于停滞产品的库存解决方式
    //      在满足市场供需的情况之下,库存越少越好
    //02.Scanf不是一个线程安全的函数
    //  1.所以须要手动进行安全检查
    //  2.它也是系统出现漏洞的原因之中的一个
    //03.防止进栈压栈冲突:延迟
    //  互锁:不要让生产者边生产而消费者边消费
    //  解决:生产者完毕之后消费者进行消费
    //注:以上情况不符合现实情况,现实情况之下须要解决多线程异步并发訪问冲突问题
    //04.生产者&消费者:
    //  1.环形队列的仓库,保证这个库存-->生产的是紧缺产品(随时满足库存量)
    //  2.库存一定须要填满(针对于畅销紧缺产品)
    //05.工厂设计模式:
    //  1.同一时候生产多个产品-->产品&线程开辟-->平衡调度线程
    //      工厂:多线程
    //  2.前台卖货:平衡调度
    //      库存控制,畅销与非畅销
    //  3.消费者消费:千变万化
    //注:区分(生产者与消费者)和(工厂)两种设计模式的差别:
    //  生产者与消费者:单产品
    //  工厂:多产品
    //06.链式队列(无线)&栈(有限)
    //  server几十万几百万的多线程操作
    //07.内存数据库:
    //  1.全部数据都加载内存-->发出请求
    //  2.文件加载内存
    //  3.消费者提出(需求),生产者进行(生产)
    //  4.线程不断的进行加载
    //  5.防止多线程并发訪问
    //  6.迁移到CGI: 手机查询
    //  7.多线程与队列问题-->稳定与不稳定

    程序片段(18):Queue.h+Queue.c+Main.c
    内容概要:02.链表反向队列

    ///Queue.h
    #pragma once
    
    #define DT int
    
    typedef struct node
    {
        DT data;
        struct node * pNext;
    }Node;
    
    void initQueue(Node ** ppQueue);
    
    void enQueue(Node ** ppQueue, DT data);
    
    void showQueue(Node * pQueue);
    
    DT queueGetHead(Node * pQueue);
    
    void deQueue(Node ** ppQueue);
    ///Queue.c
    #include "Queue.h"
    #include <stdlib.h>
    #include <stdio.h>
    
    void initQueue(Node ** ppQueue)
    {
        if (NULL == ppQueue)
            abort();
        *ppQueue = NULL;
    }
    
    void enQueue(Node ** ppQueue, DT data)
    {
        if (NULL == ppQueue)//无队列
            abort();
        Node * pNew = (Node *)malloc(sizeof(Node));
        pNew->data = data;
        pNew->pNext = NULL;
        if (NULL == *ppQueue)//空队列
        {
            *ppQueue = pNew;
            return;
        }
        pNew->pNext = *ppQueue;
        *ppQueue = pNew;
    }
    
    void showQueue(Node * pQueue)
    {
        if (NULL == pQueue)
            return;
        for (Node * pTmp = pQueue; NULL != pTmp; pTmp = pTmp->pNext)
        {
            printf("%3d", pTmp->data);
        }
        printf("
    ");
    }
    
    DT queueGetHead(Node * pQueue)
    {
        if (NULL == pQueue)
            abort();
        Node * pTmp = pQueue;
        while (NULL != pTmp->pNext)
        {
            pTmp = pTmp->pNext;
        }
        return pTmp->data;
    }
    
    void deQueue(Node ** ppQueue)
    {
        if (NULL == ppQueue)
            abort();
        if (NULL == *ppQueue)
            return;
        if (NULL == (*ppQueue)->pNext)
        {
            free(*ppQueue);
            *ppQueue = NULL;
            return;
        }
        Node * pTmp = *ppQueue;
        while (NULL != pTmp->pNext->pNext)
        {
            pTmp = pTmp->pNext;
        }
        free(pTmp->pNext);
        pTmp->pNext = NULL;
    }
    ///Main.c
    #include "Queue.h"
    #include <stdlib.h>
    #include <Windows.h>
    
    //01.链表反向队列:
    //  全局变量:用作跨线程通信变量
    Node * pQueue = NULL;
    
    //02.生产者消费者模式之生产者:
    //  1.时时刻刻盯着链表反向队列结构
    //  2.区分:畅销产品与非畅销产品
    //注:避免过度消耗资源的情况发生
    DWORD WINAPI producer(void * p)
    {//非畅销产品
        int i = 0;
        while (++i)
        {
            if (NULL == pQueue)
            {
                enQueue(&pQueue, i);
                printf("生产者生产了产品%3d! 
    ", i);
            }
            Sleep(1000);
        }
        return 0;
    }
    
    DWORD WINAPI consumer(void * p)
    {
        int i = 0;
        while (++i)
        {
            MessageBoxA(0, "wait", "consumer", 0);
            printf("消费者消费了%3d! 
    ", queueGetHead(pQueue));
            deQueue(&pQueue);
        }
        return 0;
    }
    
    int main01(void)
    {
        HANDLE threadArr[2] = { 0 };
        threadArr[0] = CreateThread(NULL, 0, producer, NULL, 0, NULL);
        threadArr[1] = CreateThread(NULL, 0, consumer, NULL, 0, NULL);
        WaitForMultipleObjects(2, threadArr, TRUE, INFINITE);
    
        system("pause");
    }
    
    
    
    //01.生产者与消费者(设计模式):
    //  1.链表反向队列:作为流水线
    //      数组(正向&反向)队列&数组(正向&反向)环形队列&链表(正向&反向)队列
    //  2.线程结构:生产者&消费者
    //  3.流程原理:
    //      (1).当流水线为空的时候,生产者生产
    //      (2).生产者:非畅销&畅销(视详细情况而定)
    //      (3).消费者:手动控制,能够获取随意个数
    //          设计模式:看不明确的主要原因是由于多线程
    //          单线程没有意义,多线程才有意义
    //02.生产者&消费者:
    //  1.生产"紧缺"产品&生产"非紧缺"产品
    //  2.生产者&消费者所做事情:
    //      (1),生产者时时刻刻检測数据结构是否已经填充满了
    //          没有满须要插入数据-->链式队列:锁定数目即可了(防止无限仓库产生)
    //          理论上都不推荐使用链式队列:由于过渡消耗资源
    //          -->链式栈不存在满的情况:能够进行无限拓展
    //      (2).用于软件开发的两种情况:
    //          1).生产&消费分开做
    //          2).工厂模式更加复杂(不同类型的生产者与消费者模式)
    //03.理解生产者与消费者
    //  1.生产者须要保证至少有一个
    //  2.消费者的消费情况是随机消费的
    //  3.消费者须要配合生产着
    //      一个入队,一个出队[消费者的消费是个不确定的数据]
    //  4.线程通信中间使用最多的是什么?
    //          事件&相互排斥量&信号量

    程序片段(19):Queue.h+Queue.c+01.Event通信(生产者消费者).cpp+02.Semaphore通信(生产者消费者).c
    内容概要:03.生产者与消费者模式

    ///Queue.h
    #pragma once
    
    #define DT int
    
    typedef struct node
    {
        DT data;
        struct node * pNext;
    }Node;
    
    void initQueue(Node ** ppQueue);
    
    void enQueue(Node ** ppQueue, DT data);
    
    void showQueue(Node * pQueue);
    
    DT queueGetHead(Node * pQueue);
    
    void deQueue(Node ** ppQueue);
    ///Queue.c
    #include "Queue.h"
    #include <stdlib.h>
    #include <stdio.h>
    
    void initQueue(Node ** ppQueue)
    {
        if (NULL == ppQueue)
            abort();
        *ppQueue = NULL;
    }
    
    void enQueue(Node ** ppQueue, DT data)
    {
        if (NULL == ppQueue)//无队列
            abort();
        Node * pNew = (Node *)malloc(sizeof(Node));
        pNew->data = data;
        pNew->pNext = NULL;
        if (NULL == *ppQueue)//空队列
        {
            *ppQueue = pNew;
            return;
        }
        pNew->pNext = *ppQueue;
        *ppQueue = pNew;
    }
    
    void showQueue(Node * pQueue)
    {
        if (NULL == pQueue)
            return;
        for (Node * pTmp = pQueue; NULL != pTmp; pTmp = pTmp->pNext)
        {
            printf("%3d", pTmp->data);
        }
        printf("
    ");
    }
    
    DT queueGetHead(Node * pQueue)
    {
        if (NULL == pQueue)
            abort();
        Node * pTmp = pQueue;
        while (NULL != pTmp->pNext)
        {
            pTmp = pTmp->pNext;
        }
        return pTmp->data;
    }
    
    void deQueue(Node ** ppQueue)
    {
        if (NULL == ppQueue)
            abort();
        if (NULL == *ppQueue)
            return;
        if (NULL == (*ppQueue)->pNext)
        {
            free(*ppQueue);
            *ppQueue = NULL;
            return;
        }
        Node * pTmp = *ppQueue;
        while (NULL != pTmp->pNext->pNext)
        {
            pTmp = pTmp->pNext;
        }
        free(pTmp->pNext);
        pTmp->pNext = NULL;
    }
    ///01.Event通信(生产者消费者).cpp
    #include "Queue.h"
    #include <stdio.h>
    #include <stdlib.h>
    #include <Windows.h>
    
    Node * pQueue = NULL;
    
    DWORD WINAPI producer(void * p)
    {
        enQueue(&pQueue, 1);
        int i = 1;
        while (++i)
        {
            HANDLE event1 = OpenEvent(EVENT_ALL_ACCESS, TRUE, L"consumer");
            WaitForSingleObject(event1, INFINITE);
            printf("生产者生产了%3d! 
    ", i);
            enQueue(&pQueue, i);
        }
        return 0;
    }
    
    DWORD WINAPI consumer(void * p)
    {
        int i = 0;
        while (++i)
        {
            MessageBoxA(0, "wait", "wait", 0);
            printf("消费者消费了%3d! 
    ", queueGetHead(pQueue));
            deQueue(&pQueue);
            HANDLE event1 = OpenEvent(EVENT_ALL_ACCESS, TRUE, L"consumer");
            SetEvent(event1);
        }
        return 0;
    }
    
    int main01(void)
    {
        HANDLE event1 = CreateEvent(NULL, FALSE, FALSE, L"consumer");
    
        HANDLE threadArr[2] = { 0 };
        threadArr[0] = CreateThread(NULL, 0, producer, NULL, 0, NULL);
        threadArr[1] = CreateThread(NULL, 0, consumer, NULL, 0, NULL);
        WaitForMultipleObjects(2, threadArr, TRUE, INFINITE);
    
        CloseHandle(event1);
        system("pause");
        return 1;
    }
    
    
    
    //01.消费完毕之后设置事件的触发
    //02.每秒钟进行检測,浪费资源
    //03.事件的关键步骤:
    //  CloseHandle(event);
    //04.在一个线程里面不须要死循环:
    //  由于它在这儿i不断的进行自增,添加的次数不确定
    //05.事件通信&信号量通信
    ///02.Semaphore通信(生产者消费者).c
    #include "Queue.h"
    #include <stdio.h>
    #include <stdlib.h>
    #include <Windows.h>
    
    Node * pQueue = NULL;
    
    DWORD WINAPI producer(void * p)
    {
        HANDLE sema = OpenSemaphoreA(SEMAPHORE_ALL_ACCESS, TRUE, "consumer");
        enQueue(&pQueue, 1);
        int i = 1;
        while (++i)
        {
            WaitForSingleObject(sema, INFINITE);
            printf("生产者生产了%3d! 
    ", i);
            enQueue(&pQueue, i);
        }
        return 0;
    }
    
    DWORD WINAPI consumer(void * p)
    {
        HANDLE sema = OpenSemaphoreA(SEMAPHORE_ALL_ACCESS, TRUE, "consumer");
        int i = 0;
        while (++i)
        {
            MessageBoxA(0, "wait", "consumer", 0);
            printf("消费者消费了%3d! 
    ", queueGetHead(pQueue));
            deQueue(&pQueue);
            ReleaseSemaphore(sema, 1, NULL);
        }
        return 0;
    }
    
    int main02(void)
    {
        HANDLE sema = CreateSemaphoreA(NULL, 0, 1, "consumer");
    
        HANDLE threadArr[2] = { 0 };
        threadArr[0] = CreateThread(NULL, 0, producer, NULL, 0, NULL);
        threadArr[1] = CreateThread(NULL, 0, consumer, NULL, 0, NULL);
        WaitForMultipleObjects(2, threadArr, TRUE, INFINITE);
    
        CloseHandle(sema);
        system("pause");
    }
    
    
    
    //01.信号量解决生产者与消费者问题:
    //  C++称之为工厂设计模式
    //02.事件-->相互排斥量解决线程通信问题:
    //  事件-->信号量问题分析
    //03.设计模式结合多线程比較好理解
    //  两个变量之间的两方通信规则
  • 相关阅读:
    Android简易抽屉效果
    apk 下载并自动安装
    python2.7_1.3_获取远程设备的IP地址
    python2.7_1.2_打印设备名和IPv4地址
    python2.7_1.14_编写一个简单的回显客户端/服务器应用
    python2.7_1.13_编写一个SNTP客户端
    C语言程序转换为Python语言
    5分钟弄懂Docker!
    让正则表达式变简单(PythonVerbalExpressions)
    python sqlalchemy-migrate 使用方法
  • 原文地址:https://www.cnblogs.com/yjbjingcha/p/7135965.html
Copyright © 2011-2022 走看看