20160227.CCPP體系詳解(0037天)

尹成發表於2016-03-27

程式片段(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! \n");//資訊通訊內容
    Sleep(1000);//資訊傳遞時間
    SetEvent(eventArrA[0]);//提示資訊傳到
    int i = 1;
    while (++i)
    {
        WaitForSingleObject(eventArrA[1], INFINITE);//等待資訊傳到
        printf("海華第%02d次說:i love you fangFang, please help me debug! \n", 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! \n", 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.什麼是死鎖?
//  編寫事件的時候最容易遇到死鎖的事情!
//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! \n");//發出通訊內容
    Sleep(1000);//模擬通訊時間
    SetEvent(eventArrB[0]);//提示通訊到達
    int i = 1;
    while (++i)
    {
        WaitForSingleObject(eventArrB[3], INFINITE);
        memset(strB, '\0', 256);
        sprintf(strB, "海華第%02d次說:i love you fangFang, please help me debug! \n", 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): \n", i);
            printf("\t%s \n", strB);
            Sleep(1000);
            SetEvent(eventArrB[1]);
            status = 1;
        }
        else
        {
            WaitForSingleObject(eventArrB[2], INFINITE);
            printf("媒婆傳遞芳芳通訊內容(傳遞次數:%02d): \n", i);
            printf("\t%s \n", 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, '\0', 256);
        sprintf(strB, "王芳第%02d次說:sorry! but i love you! \n", 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! \n", *pChr);
    if ('A' == *pChr)
    {
        MessageBox(0, TEXT("1"), TEXT("1"), 0);//暫停通知執行緒
        sprintf(strC, "海華女友%c speak:xiaohuahua lovely! \n", *pChr);//訊息通知內容
        SetEvent(eventArrC[0]);//發出事件通知
        MessageBox(0, TEXT("1"), TEXT("1"), 0);//暫停通知執行緒
        sprintf(strC, "海華女友%c speak:xiaohuahua lovely! \n", *pChr);//訊息通知內容
        SetEvent(eventArrC[0]);//發出事件通知
    }
    int i = 0;
    while (++i)
    {
        WaitForSingleObject(eventArrC[0], INFINITE);//等待事件通知
        printf("haiHua's girl friend %c read %s! \n", 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! \n", *pInt);
    HANDLE mySema = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, id);//指定範圍檢索指定名稱的訊號量
    if (mySema)//判斷訊號量是否存在
    {
        printf("worker:%d is waiting! \n", *pInt);//表示當前執行緒處於判定訊號量狀態
        Sleep(1000);
        if (WaitForSingleObject(mySema, INFINITE) == WAIT_OBJECT_0)//等待空位為0
        {//如果訊號空位為0,也就是無法開啟非同步執行緒的情況
            printf("worker:%d is getting! \n", *pInt);//此時只有當前執行緒獲得CPU執行權,其它執行緒無法獲取CPU可執行權
            Sleep(3000);
            printf("worker:%d is leaving! \n", *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("啟用狀態! \n");
    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("開啟訊號量失敗! \n");
    }
}

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 \n", 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 \n", 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("開始寫入! \n");
    AcquireSRWLockExclusive(&g_lock);
    for (int i = 0; i < 100000; ++i)
    {
        --num;
        //Sleep(10);
    }
    ReleaseSRWLockExclusive(&g_lock);
    printf("結束寫入! \n");
    return 0;
}

DWORD WINAPI read(void * p)
{
    printf("讀取狀態! \n");
    AcquireSRWLockShared(&g_lock);
    int i = 0;
    while (1)
    {
        ++i;
        Sleep(1000);
        printf("第%d秒, num = %d \n", i, num);//由於寫入狀態鎖定了,因此這裡的讀取狀態,無法讀取到資料
        if (20 == i)
            break;
    }
    ReleaseSRWLockShared(&g_lock);
    printf("讀取結束! \n");
    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 \n", 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)成功! \n");
    char chr = getch();//實時獲取單個字元
    //02.釋放互斥量:
    //  相當於發出通知
    ReleaseMutex(myMutex);
    //03.關閉互斥量:
    CloseHandle(myMutex);
    system("pause");
}



//01.關於跨程式的驅動訪問:核心物件
//  無論是Windows還是Linux都是存在互斥量說法
//02.如果是跨程式的話:
//  建立跨程式的Mutext需要有名稱(便於全域性訪問)
//03.編寫網路程式的時候:
//  既需要編寫客戶端也需要編寫網路端
//  -->編寫兩個程式的時代
//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("獲取互斥量失敗! \n");
    //  system("pause");
    //  return;
    //}
    printf("等待狀態! \n");
    //02.控制互斥量的等待時間:
    //  先獲得互斥量-->設定等待狀態時間(等待指定的時間範圍!)
    DWORD res = WaitForSingleObject(myMutex, 10000);//設定等待狀態
    switch (res)
    {
    case WAIT_OBJECT_0:
        printf("收到跨程式訊號! \n");
        break;
    case WAIT_TIMEOUT:
        printf("等待跨程式訊號超時! \n");
        break;
    case WAIT_ABANDONED:
        printf("另外一個程式發生終止!結束跨程式訊號等待狀態! \n");
        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建立成功! \n");
    char chr = getch();
    SetEvent(myEvent);//設定事件
    printf("傳送跨程式Event事件! \n");

    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獲取失敗! \n");
        system("pause");
        return;
    }
    printf("跨程式Event等待狀態! \n");
    DWORD res = WaitForSingleObject(myEvent, 10000);
    switch (res)
    {
    case WAIT_OBJECT_0:
        printf("跨程式Event收到狀態! \n");
        break;
    case WAIT_TIMEOUT:
        printf("跨程式Event超時狀態! \n");
        break;
    case WAIT_ABANDONED:
        printf("另外一個程式已經中止! \n");
        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訊號量建立成功! \n");
    char chr = getch();
    ReleaseSemaphore(mySema, 1, NULL);
    printf("跨程式Semaphore發出訊號! \n");

    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建立失敗! \n");
        system("pause");
        return;
    }
    printf("跨程式Semaphore等待狀態! \n");
    DWORD res = WaitForSingleObject(mySema, 10000);
    switch (res)
    {
    case WAIT_OBJECT_0:
        printf("跨程式Semaphore通訊收到! \n");
        break;
    case WAIT_TIMEOUT:
        printf("跨程式Semaphore通訊超時! \n");
        break;
    case WAIT_ABANDONED:
        printf("另外一個程式已經中止! \n");
        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次! \n", 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("定時器建立失敗! \n");
    }
    //設定定時器特點
    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秒之後開始幹活兒! \n");
        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! \n");
    }
    else
    {
        printf("wait no! \n");
    }

    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("\ni=%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("\ni=%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("\n");
}

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 \n", 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("\n");
}

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 \n", 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("\n");
}

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("\n");
}

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 \n", circleQueueGetHead(&circleQueue));
        deCircleQueue(&circleQueue);
        showCircleQueue(&circleQueue);
    }

    system("pause");
}

CircleQueue circleQueue = { 0 };

DWORD WINAPI producer(void * p)
{
    printf("生產者第01次執行生產任務! \n");
    int data = 0;
    while (!circleQueueIsFull(&circleQueue))
    {//生產者:一次性補充滿庫存黁量
        enCircleQueue(&circleQueue, ++data);
        printf("生產者生產了%3d! \n", 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次執行生產任務! \n", i);
        while (!circleQueueIsFull(&circleQueue))
        {
            enCircleQueue(&circleQueue, ++data);
            printf("生產者生產了%3d! \n", 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次執行消費任務! \n", i);
        int num = 9;
        for (int j = 0; j < num; ++j)
        {
            if (!circleQueueIsEmpty(&circleQueue))
            {
                printf("消費者消費了%3d! \n", 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.鏈式佇列(無線)&棧(有限)
//  伺服器幾十萬幾百萬的多執行緒操作
//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("\n");
}

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! \n", i);
        }
        Sleep(1000);
    }
    return 0;
}

DWORD WINAPI consumer(void * p)
{
    int i = 0;
    while (++i)
    {
        MessageBoxA(0, "wait", "consumer", 0);
        printf("消費者消費了%3d! \n", 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("\n");
}

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! \n", i);
        enQueue(&pQueue, i);
    }
    return 0;
}

DWORD WINAPI consumer(void * p)
{
    int i = 0;
    while (++i)
    {
        MessageBoxA(0, "wait", "wait", 0);
        printf("消費者消費了%3d! \n", 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! \n", 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! \n", 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.設計模式結合多執行緒比較好理解
//  兩個變數之間的雙方通訊規則

相關文章