20160226.CCPP體系詳解(0036天)
程式片段(01):01.多執行緒.c+02.多執行緒操作.c
內容概要:多執行緒
///01.多執行緒.c
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
#include <process.h>
//01.執行緒任務函式剖析:
// 1."封裝"執行緒任務程式碼
// 2.MessageBox();作用:
// 用於"阻塞"當前執行緒的繼續執行狀態
// 也就是暫停執行緒任務程式碼的執行
//02.MessageBox();帶參巨集的作用:
// 1.彈出一個訊息盒子
// 2.該帶參巨集位於那條執行緒任務函式當中,就會在
// 該執行緒之上產生同步效果,從而阻塞該條執行緒
//注:MessageBox();帶參巨集和TEXT();帶參巨集結合可以
// 解決字符集相容性問題(讓任何文字自動使用本地字符集)
void thTaskFun(void * pArr)
{
MessageBox(0, TEXT("1"), TEXT("1"), 0);
}
//03.關於開啟執行緒函式(_beginthread();)函式的解析:
// 1.標頭檔案:process.h
// 2.格式:_beginthread(arg1, arg2, arg3);
// 3.引數:
// arg1:函式指標(表象)<->執行緒任務程式碼(實質)
// arg2:棧的尺寸,0表示預設情況(拷貝主執行緒棧尺寸作為新開啟執行緒所佔用的棧尺寸)
// arg3:開啟執行緒之後,向執行緒任務函式所傳遞的引數(資料封裝體的地址)
//注:資料封裝體的地址,便於跨函式訪問多個引數!+提升效率
int main01(void)
{
MessageBox(0, "2", "2", 0);//此處將會阻塞主執行緒
_beginthread(thTaskFun, 0, NULL);//用於開啟一條非同步執行的執行緒
_beginthread(thTaskFun, 0, NULL);
_beginthread(thTaskFun, 0, NULL);
_beginthread(thTaskFun, 0, NULL);
system("pause");
}
///02.多執行緒操作.c
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
#include <process.h>
//01.執行緒狀態管理:
// 執行緒狀態:凍結(暫停)+解凍(繼續)
//注:_beginthread();不太注重程式碼格式要求,CreateThread();注重程式碼格式要求
void print(void * p)
{
int i = 0;
while (1)
{
printf("%d \n", ++i);
Sleep(1000);
}
}
//02.阻塞執行緒和阻塞程式:
// 阻塞執行緒
// 1.阻塞執行緒之後,只是當前執行緒的後續程式碼暫停執行狀態
// 2.任何情況之下都可以觸發阻塞執行緒的狀態
// 阻塞程式:
// 1.阻塞程式之後,程式之內的所有程式碼全部處於暫停執行執行狀態
// 2.CPU在為單個程式只開啟單條執行緒的情況之下才能出現阻塞程式現象
//注:Debug除錯模式狀態下,無論是單執行緒還是多執行緒,只要阻塞了主執行緒
// 就會出現阻塞程式的現象,從而其它執行緒無法得到具體的執行
//注:只有在多執行緒的情況之下才具備阻塞執行緒和阻塞程式之間的區別
// 因為在單執行緒的情況之下阻塞執行緒就等同於阻塞程式
//注:阻塞主執行緒的情況之下,才有可能阻塞程式,從而讓其它所有的程式碼得不到
// 繼續執行的機會(當然,是在其他執行緒沒有啟動的情況之下)
//注:凍結主執行緒的情況之下,在除錯模式下等同於阻塞程式,在這種情況之下可以
// 方便與實施對其他執行緒的狀態切換操作
//03.阻塞執行緒的兩種常見方式:
// MessageBox();帶參巨集
// system("pause");
int main02(void)
{
_beginthread(print, 0, NULL);
system("pause");//(Debug除錯模式下)暫停主執行緒等同於暫停整個程式,便於對其它執行緒的狀態切換操作
system("pause");
system("pause");
system("pause");
}
//01.執行緒狀態:凍結(暫停)&解凍(繼續)
//02.執行緒休眠:Sleep();用於當前執行緒任務函式之中,休眠當前執行緒
//03.非同步執行緒:_beginthread(run,0,NULL);
// run:函式指標
// 0:堆疊尺寸
// NULL:引數列表
//04.阻塞執行緒:
// MessageBox();
// system("pause");
//05.凍結&解凍[執行緒管理方面的重要概念]
// 實質:在於是否存在於系統所控制的環形佇列當中<=>是否處於等待CPU執行的狀態
// 特點:由於CPu存在的固定的CPU執行頻率,因此CPU對環形佇列當中的執行緒任務分配
// 相應所需的時間片,按照順序進行環形佇列當中的執行緒任務函式內容切換
//注:一條執行緒對應於一個環形佇列,對應於一組執行緒任務序列
//06.作業系統管理執行緒的特點:
// 1.作業系統為每個程式的相應程式碼片段分配相應的時間片
// 2.該時間片模型的本質就是一個環形佇列(執行緒<->佇列)
// 3.CPU針對於每一個時間片進行切換訪問
//07.關於同步與非同步的概念:
// 1.單條執行緒根本不存在同步與非同步的概念,只是會出現同步與非同步的現象
// 2.只有多執行緒的情況之下,才存在同步與非同步的概念
//注:嚴格區分概念和現象
//08.如何進行執行緒凍結&解凍狀態的演示?
// 1.採用兩條執行緒
// 2.主執行緒的阻塞狀態會導致副執行緒暫時無法獲得CPU的執行權
// 3.在主執行緒的阻塞狀態之下進行副執行緒的執行緒狀態管理演示[凍結&解凍]
//09.測試多執行緒:多執行緒的常用操作
// 例子-->細節-->通訊-->佇列
//10.作業系統特點:
// 1.作業系統:PC&Phone
// 2.由於CPU存在CPU執行頻率,所以作業系統分配給執行緒的任務程式碼時間片大小有限,
// 也就是環形佇列總時間片固定,只不過是迴圈進行執行,所以應用程式不會存在執
// 行不到的情況[CPU重新整理環形佇列的頻率]
// 3.單核CPU一個時刻只會執行一條執行緒,根據概率進行切換
// 4.關於多執行緒的假象問題:
// 單核CPU只能出現多執行緒的假象情況,不會出現多多執行緒的真相情況
// 也就是說某一時刻,單核CPU只會處理一條執行緒,多核CPU才能出現處理多執行緒情況
// 5.作業系統通過執行緒環形佇列實現對CPU隨機切換動作的排程[隨機性]
// 6.所有的執行緒都放置在同一個作業系統所管理的環形佇列當中,然後讓作業系統對CPU進行隨機排程執行
// CPU排程執行某條執行緒-->從而指向某條執行緒的任務程式碼
//11.凍結&解凍:
// 凍結:休眠&從環形佇列當中退出
// 解凍:執行&進入到環形佇列當中-->等待執行(不一定立即得到CPU的執行權)
// 執行權:CPU切換到執行緒的執行狀態-->執行
程式片段(02):01._beginthread.c+02.CreateThread.c
內容概要:多執行緒衝突問題
///01._beginthread.c
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
#include <time.h>
#include <process.h>
//01.臨界區:CRITICAL_SECTION
// 1.解決多執行緒併發訪問衝突問題
// 2.所允許的多執行緒併發訪問數64
// 3.通常情況之下需要將臨界區定義為全域性變數:
// 為的是讓多條執行緒識別同一個臨界區!(防止互鎖)
//注:臨界區情況之下,如果併發訪問數目大於64
// 那麼多執行緒併發訪問效果將會失效
//注:臨界區的實質是一個結構體型別,它位於Windows.h
// 標頭檔案中進行的型別定義
//注:C語言的全域性變數不會進行自動初始化操作,CPP語言
// 會進行預設的自動初始化操作
static CRITICAL_SECTION cs;
//02.全域性變數:用於多執行緒之間的通訊資訊描述
// 任何一個當前程式下的執行緒都可以訪問這個全域性變數
//注:全域性變數在預設的情況之下,如果被多條執行緒所併發
// 訪問,容易導致執行緒併發訪問衝突問題!
static int num = 0;
//03.多執行緒併發訪問問題詳細剖析:
// 1.理論上:100條執行緒併發計算同一個資料之後的結果應當是
// 100*100=10000,但是結果卻是達不到10000
// 2.原因是:多條執行緒同時讀取到相同的資料,再進行資料寫入
// 的時候,寫入相同的結果,因此導致計算次數-1,因此結果-1
//注:使用臨界區之後,多條執行緒在同一時刻的情況之下,只能由
// 單條執行緒進行資料的訪問(讀取和寫入)
//注:使用臨界區之後,將會有N多條執行緒處於執行狀態下:
// 其中,(N-1)條處於執行"等待"狀態下-->
// 全部都是處於"急切"等待狀態下-->
// 臨界區一旦接觸,就會隨機抽取一條執行緒得到CPU的執行權
// 其中,1條處於執行"執行"狀態下
//注:避免頻繁的使用臨界區可以加快資料的訪問效率
// 嚴格避免頻發的加解鎖情況發生(多執行緒效率提升與優化)
static void myFun(void * p)
{
for (int i = 0; i < 100; ++i)
{
EnterCriticalSection(&cs);//進入臨界區
++num;
LeaveCriticalSection(&cs);//離開臨界區
Sleep(3);//不要講時間操作放置於臨界區當中(否則極度耗費時間)
}
}
//04.如何對程式進行計時操作?
// 1.時間刻度
// 2.記錄開始
// 3.記錄結束
// 4.時間差值
//05.HANDLE型別解析:
// 1.位於Windows.h標頭檔案當中
// 2.名為控制程式碼,實質void *,也就是一個空型別的地址型別
// 只是一個地址數值的型別定義,沒有明確的解析方式
//06.要想模擬多執行緒處理操作,必須等待所有執行緒執行完畢
// 最終再進行多條執行緒的結果處理
//07.等待多條執行緒執行狀態的結束:
// 格式:WaitForMutipleObjects(arg1, arg2, arg3, arg4);
// 引數:arg1:執行緒個數+arg2:執行緒陣列+arg3:單個|全部等待+arg4:等待時間
//08.這兒處在兩個衝突問題:
// 1.同步執行:結果精確,耗費時間
// 2.非同步執行:時間精確,結果不正確
//09.解決方式:
// 臨界區:實質就是加解鎖的特點
// 新增一把鎖,必須等待當前執行緒執行狀態完畢之後,再進行解鎖,再讓下一個
// 執行緒執行臨界區當中的程式碼塊兒
// 訊號量:通過訊號提示決定是否可以進行變數的訪問
int main01(void)
{
InitializeCriticalSection(&cs);
time_t start, end;
time(&start);
HANDLE thArr[100] = { 0 };
for (int i = 0; i < 100; ++i)
{//這裡需要等待所有執行緒執行結束並退出,所以需要使用執行緒控制程式碼陣列進行統一管理
thArr[i] = _beginthread(myFun, 0, NULL);
//thArr[i] = CreateThread(NULL, 0, myFun, NULL, 0, NULL);
//這兒如果是使用等待單條執行緒執行完畢之後,再讓下一個執行緒執行,這樣會導致等待時間較長
//效率低下,等待一個一個的執行緒執行結束,如果修改成功,同步收取,一個一個的執行,防止競爭
//關係的產生,造成最終的處理結果丟失
}
WaitForMultipleObjects(100, thArr, TRUE, INFINITE);
time(&end);
printf("difftime = %lf \n", difftime(end, start));
printf("num = %d \n", num);
DeleteCriticalSection(&cs);
system("pause");
}
//01.多執行緒問題:凍結&解凍之後
// 1.通過佇列,通過一些額外的方式對多執行緒進行一些額外的操作
// 2.多執行緒衝突問題:開發中間經常遇到的問題
// 多條執行緒同時訪問一個全域性變數,容易出現多執行緒併發訪問問題
//02.全域性變數:
// 任何一條執行緒都可以進行該變數的訪問,所以多執行緒在訪問的時候,非常容易出問題,所以在進行大資料處理
// 的時候,將所有的資料全部載入進記憶體之後,如果採用多執行緒併發訪問,就需要首要解決這個問題!
//03.解決多執行緒併發訪問問題:
// 1.同步單條執行緒:缺點就是單條執行緒的操作時間過長!->效率低下,耗費時間
// 2.因此需要使用臨界區進行各條不同的執行緒之間的同一時刻的遮蔽操作
//04.計時函式程式設計:
// 1.#include <time.h>
// 2.定義->起始(時刻)->終止(時刻)->差距->顯示
// time_t start,end;
// time(&start);//記錄起始
// time(&end);//記錄結束
// difftime(end,start);//統計差距
// %lf//顯示結果
//05.定義臨界區:
// 1.修改字尾為.cpp
// 2.定義步驟:
// 宣告臨界區:
// CRITICAL_SECTION cs;//實質就是一個"結構體"
// 解釋:LONG LockCount;LONG RecursionCount;[Lock訊號+方式]
// 特點:該臨界區既可以應用於C++同樣也可以應用於C當中
// 原則:一般情況之下,我們應用多執行緒都是採用CPP
// 進入臨界區:
// EnterCriticalSection(&cs);
// 離開臨界區:
// LeaveCriticalSection(&cs);
// 刪除臨界區:
// 特點:臨界區不是一個變數,實質上是作業系統,讓我們的這個執行緒進入到臨界區鎖定狀態
// 臨界區位於Windows.h標頭檔案當中(多執行緒最高階是由作業系統完成排程的)
// DeleteCriticalSection(&cs);
//06.臨界區原理:保證精確操作
// 1.並行操作情況之下,效率提升,但是結果不精確-->推出臨界區概念:
// 臨界區用於解決同步單條執行緒的效率低下問題,執行緒進入臨界區之後,該條執行緒獨享程式碼執行權
// 2.臨界區的概念:媒婆特點
//07.CreateThread();與_beginthread();的區別
// CreateThread();使用的是HANDLE
// _beginthead();使用的是int
// 屬於process.h執行緒庫當中的一個執行緒函式宣告,這裡只能給你發生互鎖的情況,但是不能解決多執行緒的衝突問題,所以這兒需要使用
// CreateThead();執行緒訪問的時候發生的衝突情況[只能加鎖,彈出多執行緒訪問異常]
//08.程式碼除錯的方式:
// 逐步放開部分程式碼進行觀察
///02.CreateThread.c
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
#include <time.h>
//01.定義"臨界區"為全域性變數:
// 原因:讓多條執行緒識別同一個臨界區
CRITICAL_SECTION cs;
//02.定義"多執行緒"的通訊資訊:
// 多執行緒資訊容易導致多執行緒併發訪問衝突問題
int num = 0;
//03._beginthread();執行緒任務函式格式
//void myFun(void * p)
//{
// for (int i = 0; i < 100; ++i)
// {
// EnterCriticalSection(&cs);
// ++num;
// LeaveCriticalSection(&cs);
// }
//}
//04.CreateThread();執行緒任務函式格式
// DWORD:unsigned long
// WINAPI:宣告讓作業系統來排程這條執行緒[作業系統排程標識]
DWORD WINAPI myFun(void * p)
{
EnterCriticalSection(&cs);
for (int i = 0; i < 100; ++i)
{
++num;
}
LeaveCriticalSection(&cs);
return 0;
}
int main02(void)
{
InitializeCriticalSection(&cs);
time_t start, end;
time(&start);
HANDLE thArr[100] = { 0 };
for (int i = 0; i < 100; ++i)
{
thArr[i] = CreateThread(NULL, 0, myFun, NULL, 0, NULL);
//WaitForSingleObject(thArr[i], INFINITE);
}
WaitForMultipleObjects(100, thArr, TRUE, INFINITE);
time(&end);
printf("difftime = %lf \n", difftime(end, start));
printf("num = %d \n", num);
DeleteCriticalSection(&cs);
system("pause");
}
//01.臨界區:
// CRITICAL_SECTION:處於"核心部位",它的實現是依賴於作業系統的"核心"
// 所以這裡建立執行緒的方式使用CreateThread();[系統特點&HANDLE]
//02.由於CreateThread();函式來源於Windows內部,所以該函式的引數會很多,於是進行函式改良
// 宣告:DWORD WINAPI
// 建立:CreateThread(NULL,0,myfun,NULL,0,NULL);
// NULL:附加資訊,安全等級
// 0:堆疊大小:
// myfun:函式指標
// NULL:引數列表
// 0:優先等級
// NULL:執行緒編號
//03.這兒的臨界區還是存在一定的問題
程式片段(03):_beginthread.c
內容概要:執行緒池
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
#include <process.h>
//01.建立執行緒的兩種方式:
// 實質:CreateThread();
// 表象:_beginthread();
//02.結束執行緒的三種方式:
// 內部:
// endthread();
// exitthread();
// 外部:
// terminate();
//03.執行緒狀態的兩種控制方式:
// 凍結:SuspendThread(hd);
// 解凍:ResumeThread(hd);
//04.CreateThread();建立執行緒所需執行緒函式格式:
// DWORD:unsigned long
DWORD WINAPI myFun(void * p)
{
int i = 0;
while (++i)
{
printf("%d \n", i);
if (i > 8000)
_endthread();
Sleep(1000);
}
return 0;
}
//05.主執行緒與輔助執行緒:
// 1.主執行緒:處理主函式的執行緒
// 在整個程式當中起到主導作用,管控整個程式內部的其它所有執行緒
// 2.輔助執行緒:處理其它函式的執行緒
//注:除錯模式下,如果"斷點"主執行緒,那麼其它執行緒將會暫停執行狀態
// 暫停主執行緒就相當於佔用了品目重新整理執行緒,於是螢幕將不會發生變化
int main01(void)
{
HANDLE th = CreateThread(NULL, 0, myFun, NULL, 0, NULL);
system("pause");
SuspendThread(th);//凍結
system("pause");
ResumeThread(th);//解凍
system("pause");
//ExitThread(0);//錯誤位置
TerminateThread(th, 0);//外部強行退出
//_endthread();
//system("pause");
system("pause");
}
//01.操作執行緒:執行緒排程問題
// 開啟-->凍結-->解凍-->內部結束-->外部結束
//02.重點內容:凍結&解凍
// 操作單條執行緒
//03.CreateThead();執行緒
// 1.DWORD WINAPI&return 0;
// 2.HANDLE hd=CreateThead(NULL,0,fun,NULL,0,NULL);
// NULL:安全等級
// 0:堆疊大小,預設為0就是拷貝自身,每個執行緒的棧記憶體都是獨立的,而非共享的
// fun:函式指標
// NULL:引數列表
// 0:優先等級
// NULL:執行緒標識
//04.管理排程執行緒池:
//05.演示或者操作執行緒的時候,一般情況之下不要使用IDE進行演示:
// 1.注意執行緒的優先等級
// 2.關閉執行緒的特點[主執行緒與副執行緒的區別]
// 3.程式依然存在:
// 如果是程式自己結束就會自動結束所有執行緒並結束該程式,但是這個工具是應用
// 底層作業系統核心驅動來完成的,所以它的操作方法比較高階,於是手動結束的時候
// 執行緒掛掉,但是程式卻沒有退出
//06.結束執行緒的三種方式:
// 內部結束:_endthread();+exitthread();
// 外部結束:terminatethread();
// 全部結束:CloseThreadPool();//關閉執行緒池
//07.凍結執行緒與解凍執行緒:
// SuspendThread();+ResumeThead();
//08.強制結束一條執行緒:資料容易丟失
// 自我結束語強行結束:return
// 非強制:內部
// _endThread();//指定執行緒
// ExitThread(0);//預設引數
// 強制:外部
// TerminateThead(hd,0):
// 0代表退出的時候攜帶一個編號
//09.執行緒內部的等待採用死迴圈進行實現
//10.外部操作執行緒完畢
程式片段(04):01._beginthread.c+02.CreateThead.c
內容概要:執行緒池
///01._beginthread.c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <process.h>
#include <Windows.h>
//01.提取建立執行緒時期由建立函式傳遞給執行緒函式的引數:
// 1.讀取建立執行緒傳遞的引數列表,需要進行型別轉換
// 2.空型別的地址(乾地址)-->強制型別轉換-->獲取資料
void run(void * p)
{
char str[10] = { 0 };
sprintf(str, "備胎%d", *(int *)p);
printf("%s \n", str);
}
//02.防止多執行緒併發訪問衝突的方式:
// 1._beginthead(run,0,&a[i]);解釋
// (1).多個執行緒不可以同時訪問同一個地址,防止多執行緒訪問衝突
// (2).引數列表說明:
// run:函式指標
// 0:堆疊大小
// &a[i]:引數列表-->這兒的每條執行緒所訪問到的資料都不一樣
// 獨立訪問而非併發訪問
// 2._beingthead();函式的返回值是int型別
// 因此_beginthread();和CreateThread();所採取的控制多執行緒
// 非同步操作的方式不一樣(一個是int型別的陣列,一個是HANDLE型別的陣列)
int main01(void)
{
int arr[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
for (int i = 0; i < 10; ++i)
{
int id = _beginthread(run, 0, &arr[i]);
WaitForSingleObject(id, 300);
}
system("pause");
}
///02.CreateThead.c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
DWORD WINAPI myFun01(void * p)
{
int i = 0;
while (++i)
{
printf("%d \n", i);
Sleep(1000);
}
return 0;
}
int main02(void)
{
HANDLE th = CreateThread(NULL, 0, myFun01, NULL, 0, NULL);
system("pause");
system("pause");
system("pause");
}
DWORD WINAPI myFun02(void * p)
{
char str[10] = { 0 };
sprintf(str, "備胎%d", *(int *)p);
MessageBoxA(0, str, str, 0);
return 0;
}
//01.CreateThread();方式建立執行緒詳解:
// 1.控制多條執行緒非同步執行狀態的所需陣列:
// HANDLE型別的控制程式碼陣列
// 2.建立執行緒所需的引數說明:
// NULL:安全屬性集
// 0:堆疊尺寸,預設拷貝主執行緒的棧大小,棧獨立
// myfun:函式指標
// a+i:引數地址
// 0:優先等級
// NULL:執行緒編號
int main02(void)
{
int arr[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
HANDLE thArr[10] = { 0 };
for (int i = 0; i < 10; ++i)
{
thArr[i] = CreateThread(NULL, 0, myFun02, thArr + i, 0, NULL);
//WaitForSingleObject(thArr[i], INFINITE);
}
WaitForMultipleObjects(10, thArr, FALSE, INFINITE);
system("pause");
}
程式片段(05):多執行緒.c
內容概要:操作一條執行緒
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
#include <process.h>
//01.建立一條執行緒的兩種方式:
// 底層方式:CreateThread();
// 表象方式:_beginthread();
//02.結束一條執行緒的三種方式:
// 內部方式:_endthread();+ExitThread();
// 外部方式:TerminateThread();
//03.執行緒狀態控制的兩種方式:
// 凍結:SuspendThread();
// 解凍:ResumeThread();
DWORD WINAPI myFun(void * p)
{
int i = 0;
printf("%d \n", i);
while (++i)
{
printf("%d \n", i);
if (i > 8000)
{
//_endthread();
//ExitThread(0);
}
}
return 0;
}
int main01(void)
{
HANDLE th = CreateThread(NULL, 0, myFun, NULL, 0, NULL);
system("pause");
SuspendThread(th);
system("pause");
ResumeThread(th);
system("pause");
TerminateThread(th, 0);
system("pause");
}
程式片段(06):臨界區.c
內容概要:執行緒臨界區解決執行緒衝突
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
//01.臨界區的使用與作業系統排程執行緒相關:
// 所有執行緒的本質排程還是得依賴於作業系統本身
// #include <Windows.h>--->臨界區結構體
#define N 20
int num = 0;
//02.臨界區結構體變數:
// 1.一個用於收錢,一個用於用錢
// 2.定義為全域性變數的原因是:
// 為了讓多條執行緒識別同一臨界區
CRITICAL_SECTION cs1;
CRITICAL_SECTION cs2;
//03.對臨界區的優化操作:
// 減少頻繁的進出臨界區的次數;
// 有利於提高多執行緒併發訪問的效率
DWORD WINAPI add(void * p)
{
EnterCriticalSection(&cs1);//減少進出
for (int i = 0; i < 10000; ++i)
{
//EnterCriticalSection(&cs1);//頻繁進出
++num;
//LeaveCriticalSection(&cs1);//效率低下
}
LeaveCriticalSection(&cs1);//效率提升
return 0;
}
//04.使用臨界區的注意事項:
// 1.臨界區結構體變數必須是全域性變數
// 2.臨界區結構體變數最好不要巢狀使用
DWORD WINAPI sub(void * p)
{
EnterCriticalSection(&cs2);
for (int i = 0; i < 10000; ++i)
{
--num;
}
LeaveCriticalSection(&cs2);
return 0;
}
int main(void)
{
//01.初始化臨界區結構體變數
InitializeCriticalSection(&cs1);
InitializeCriticalSection(&cs2);
//02.匿名程式碼塊兒的使用方式:
// 劃分割槽域執行程式碼(複用程式碼)
//03.要想多執行緒非同步執行狀態:
// 需要使用多執行緒控制程式碼陣列
{
HANDLE thArr[N] = { 0 };
for (int i = 0; i < N; ++i)
{
thArr[i] = CreateThread(NULL, 0, add, NULL, 0, NULL);
//WaitForSingleObject(thArr[i], INFINITE);
}
WaitForMultipleObjects(N, thArr, TRUE, INFINITE);
printf("num = %d \n", num);
}
{
HANDLE thArr[N] = { 0 };
for (int i = 0; i < N; ++i)
{
thArr[i] = CreateThread(NULL, 0, sub, NULL, 0, NULL);
}
WaitForMultipleObjects(N, thArr, TRUE, INFINITE);
printf("num = %d \n", num);
}
//04.釋放臨界區結構體變數
DeleteCriticalSection(&cs1);
DeleteCriticalSection(&cs2);
system("pause");
}
//01.什麼是臨界區?
// 1.臨界區是用於解決多執行緒併發訪問衝突問題的
// 2.包括Cocos2dx當中的C++封裝執行緒,基本上都是
// 我們現在所使用的同步方式(這裡的執行緒操作方式就是實質)
// 3.臨界區所支援的最大執行緒個數為64,Windows作業系統之上
// 執行緒個數限制,但是伺服器作業系統上面沒有限制
// 4.臨界區的使用步驟:
// 建立-->初始化-->進入-->離開-->刪除
//02.執行緒通訊-->程式通訊:
// 併發訪問錯誤:收錢特點,收不過來之後
//03.防止多執行緒併發訪問錯誤:
// 1.WaitForSingleObject(hd[i]);等待單個執行緒逐個退出-->缺點:效率低下
// 2.避免多執行緒併發訪問問題,並且提高多執行緒操作效率的方式:
// 臨界區-->跑到最近的位置-->逐個給錢[以前的情況是一個一個的處理]
// 臨界區&互斥量
//04.臨界區的完整使用過程:
// 建立臨界區:全域性宣告
// CRITICAL_SECTION cs;
// 初始化臨界區:Main();
// InitializeCriticalSection(&cs);
// 使用臨界區:
// 進入:EnterCriticalSection();
// 退出:LeaveCriticalSection();
// 釋放臨界區:DeleteCriticalSection();
// 1.因為臨界區結構體變數內部存在指標情況,會耗費記憶體,所以需要釋放臨界區
// 2.還有原因就是因為我們這裡申請的是作業系統排程(避免過度佔用操作作業系統資源)
//05.執行緒庫的初始化特點:
// C語言不會自動進行初始化,C++會進行自動的初始化動作
//06.解決多執行緒併發訪問衝突的兩種方式最大特點:
// 同步方式:
// 1.效率低下
// 2.一次只有一條執行緒處於執行狀態
// 臨界方式:
// 1.效率優化
// 2.一次擁有多條執行緒處於執行狀態
//注:臨界方式一次有(N-1)條執行緒處於等待執行狀態,但是總有一條是處於執行狀態
//注:同步方式和臨界方式最大的區別就在於處於等待執行狀態的執行緒數目不同
// 同步方式:沒有等待執行狀態的執行緒
// 臨界方式:用於等待執行狀態的執行緒
// 相當於一個有預熱狀態,一個沒有預熱狀態!
程式片段(07):01.事件.c+02.事件.c+03.事件.c
內容概要:執行緒通訊事件機制
///01.事件.c
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
//01.事件:確定為3個
HANDLE eventArrA[3] = { 0 };
HANDLE threadArrA[3] = { 0 };
//02.同步:兩個執行緒-->三個執行緒-->多個執行緒
DWORD WINAPI firstThread(void * p)
{
MessageBoxA(0, "1", "1", 0);
printf("第一個執行緒執行完畢! \n");
SetEvent(eventArrA[0]);//設定事件(發出Event通知)
return 0;
}
//03.事件用於多條執行緒之間的通訊:
// 可以用於控制多執行緒的情況之下,各條執行緒之間的執行順序
//注:設定事件相當於發出通知,等待事件相當於處理通知
DWORD WINAPI secondThread(void * p)
{
//等待一個事件執行完畢[等待Event訊號的出現,再執行下一步操作]
WaitForSingleObject(eventArrA[0], INFINITE);//處理事件(等待Event通知)
MessageBoxA(0, "2", "2", 0);
printf("第二個執行緒執行完畢! \n");
return 0;
}
//04.控制程式碼型別:
// 建立事件的方式+建立執行緒的方式
int main01(void)
{
//1.初始化事件引數:
// NULL:安全等級+TRUE:人為設定+FALSE:觸發狀態+NULL:事件名稱
eventArrA[0] = CreateEvent(NULL, TRUE, FALSE, NULL);
eventArrA[1] = CreateEvent(NULL, TRUE, FALSE, NULL);
//2.兩條執行緒並行彈出盒子,現在需要控制執行緒的執行順序:
threadArrA[0] = CreateThread(NULL, 0, firstThread, NULL, 0, NULL);
threadArrA[1] = CreateThread(NULL, 0, secondThread, NULL, 0, NULL);
//3.維持多執行緒非同步執行狀態:
WaitForMultipleObjects(2, threadArrA, TRUE, INFINITE);
printf("全部等待完成! \n");
system("pause");
}
//01.執行緒同步的問題:
// 1.伺服器當中的多條執行緒:
// (1).將外部資料寫入到本地檔案
// (2).從本地檔案讀取資料到記憶體
// 2.相關問題:
// (1).沒有寫入之前無法進行讀取
// (2).但是這兩條執行緒是同時開啟的
// 3.模型:
// (1).文字-->寫入-->讀取[配置檔案特點]
// (2).先寫入本地檔案,再進行本地檔案讀取
// (3).同時開啟的時候,就需要進行多執行緒通訊,以確保執行緒的執行流程
//02.執行緒通訊:事件機制[用於管理執行緒通訊]
// 1.同時啟動的兩條執行緒,將會任意確定執行順序,所以需要執行緒排程
// 2.實現時間等待的特點(執行緒通訊)
//03.事件用作通知:CreateEvent();事件處理機制
// A:預設的安全設定0
// B:TRUE人工設定&FALSE自動設定
// C:是否進入到觸發狀態
// D:事件名稱
//04.執行流程:聊天兒系統[多事件處理]
// 1.等待執行原理的使用
// 2.建立事件,執行緒都是在主函式內完成
// (1).事件必須是全域性變數(多條執行緒訪問統一標識!)
// (2).執行緒必須是全域性變數(任意函式都可以建立執行緒)
// (3)SetEvent(e);WaitForSingleObject(e);
// 設定(觸發)+等待(處理)<->發出通知+處理通知
//05.聊天兒系統原理:
// 1.多個時間連續
// 2.多層巢狀問題
///02.事件.c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
HANDLE threadArrB[3] = { 0 };//多執行緒處理
HANDLE eventArrB[4] = { 0 };//執行緒通訊
//01.全域性變數:所有執行緒使用同一臨界區標識
// 用於解決全域性的多執行緒併發訪問衝突問題
CRITICAL_SECTION csB;//執行緒互斥
//02.全域性變數:多執行緒之間的通訊內容
// 1.代表聊條兒內容的緩衝區
// 2.memset(str, '\0', 1024);
// 暫時用不到,三方通話機制需要使用
// 3.三方通訊原理:中介者設計模式
// 海華和芳芳不會直接接觸的特點;
// 都是通過媒婆進行間接接觸的特點
char strB[1024] = { 0 };//通訊內容
//03.三方通訊原理簡介:
// 海華-->媒婆(設定事件0)
// 媒婆(處理事件0)
// 媒婆-->芳芳(設定事件1)
// 芳芳(處理事件0)
// 芳芳-->媒婆(設定事件2)
// 媒婆(處理事件2)
// 媒婆-->海華(設定事件3)
// 海華(處理事件3)
DWORD WINAPI haiHuaB(void * p)
{
int i = 1;
EnterCriticalSection(&csB);
memset(strB, '\0', 1024);//防止併發訪問衝突
sprintf(strB, "haiHua第%d次說:i love you fangfang! \n", i);//三方通訊
LeaveCriticalSection(&csB);
Sleep(1000);//不會佔用過多的互斥時間(模擬資料生成時間)
SetEvent(eventArrB[0]);//發出通知
//00:模擬第一次的資料設定(通訊主動發出者:主動者)
while (++i)
{
WaitForSingleObject(eventArrB[3], INFINITE);//等待通知(必須等待)
EnterCriticalSection(&csB);
memset(strB, '\0', 1024);//內容清空
sprintf(strB, "haiHua第%d次說:i love you fangfang! \n", i);
LeaveCriticalSection(&csB);
Sleep(1000);//模擬現實
SetEvent(eventArrB[0]);
}
return 0;
}
DWORD WINAPI ruiFuB(void * p)
{
int i = 0;
while (++i)
{
WaitForSingleObject(eventArrB[0], INFINITE);
EnterCriticalSection(&csB);
printf("媒婆給HaiHua傳遞:%s \n", strB);
LeaveCriticalSection(&csB);
Sleep(1000);
SetEvent(eventArrB[1]);
WaitForSingleObject(eventArrB[2], INFINITE);
EnterCriticalSection(&csB);
printf("媒婆傳遞FangFang:%s \n", strB);
LeaveCriticalSection(&csB);
Sleep(1000);
SetEvent(eventArrB[3]);
}
//WaitForSingleObject(eventArrB[0], INFINITE);
//printf("%s \n", strB);
//SetEvent(eventArrB[1]);
//WaitForSingleObject(eventArrB[2], INFINITE);
//printf("%s \n", strB);
return 0;
}
DWORD WINAPI fangFangB(void * p)
{
int i = 0;
while (++i)
{
WaitForSingleObject(eventArrB[1], INFINITE);
EnterCriticalSection(&csB);
memset(strB, '\0', 1024);
sprintf(strB, "fangFang第%d次說:i love you haihua! \n", i);
LeaveCriticalSection(&csB);
Sleep(1000);
SetEvent(eventArrB[2]);
}
return 0;
}
int main02(void)
{
InitializeCriticalSection(&csB);
eventArrB[0] = CreateEvent(NULL, TRUE, FALSE, NULL);
eventArrB[1] = CreateEvent(NULL, TRUE, FALSE, NULL);
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);
printf("over!!! \n");
DeleteCriticalSection(&csB);
system("pause");
}
//01.雙方通訊的原理:你發一條,我收一條,然後我再傳送一條給你,你接收我一條
//02.顯示模組兒抽離出來之後,就是一個單獨的函式,用於展示內容
// 1.同時做兩個檢測,三個訊號-->防止作業系統死鎖
// 2.說話的同時i需要進行通知,逐級進行通知
// 3.原理:說話+通知[Show進行通知]-->三部曲
// 4.大家都在等待Show-->中介者模式&媒婆模式
//03.流程分析:
// haihua傳送訊息-->媒婆接收訊息
// 媒婆收到訊息-->媒婆通知芳芳
//04..中介者模式:
// 1.haihua-->發出訊號0
// 2.zhongjiezhe-->等待訊號0--發出訊號1
// 3.wangfang-->等待訊號1--發出訊號2
// 4.zhongjiezhe-->等待訊號2-->發出訊號3
// 5.haihua-->等待訊號3--發出訊號0
//05.事件審批逐級審批原理
// 用於多條執行緒之間的逐級通訊
///03.事件.c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
//01.HANDLE:作業系統提供的控制程式碼標識,原始型別為(void *)
HANDLE threadArr[3] = { 0 };//執行緒控制程式碼陣列
HANDLE eventArr[4] = { 0 };//事件控制程式碼陣列
//02.CRITICAL_SECTION:作業系統核心提供的臨界區結構體變數
CRITICAL_SECTION cs = { 0 };//臨界區全域性變數:整個多執行緒體系都能夠識別的唯一標識
char str[1024] = { 0 };//全域性資訊公告欄{代表聊天內容的緩衝區}
//03.中介者設計模式原理:
// 海華向媒婆 設定事件0
// 媒婆從海華 等待事件0
// 媒婆向芳芳 設定事件1
// 芳芳從媒婆 等待事件1
// 芳芳向媒婆 設定事件2
// 媒婆從芳芳 等待事件2
// 媒婆向海華 設定事件3
// 海華從媒婆 等待事件3
// 海華向媒婆 設定事件0
DWORD WINAPI haiHua(void *p)//WINAPI:作業系統提供的待排程標識
{
int i = 1;//標識海華主動發言
EnterCriticalSection(&cs);//進入臨界區
memset(str, '\0', 1024);//內容清空
sprintf(str, "haiHua第%d次說:i love you Fang! \n", i);//發言內容
LeaveCriticalSection(&cs);//離開臨界區
Sleep(1000);//模擬現實通訊事件間隔
SetEvent(eventArr[0]);//發出事件通知0
while (++i)
{
WaitForSingleObject(eventArr[3], INFINITE);//等待事件通知3
EnterCriticalSection(&cs);
memset(str, '\0', 1024);
sprintf(str, "\nhaiHua第%d次說:i love you Fang! \n", i);
LeaveCriticalSection(&cs);
Sleep(1000);
SetEvent(eventArr[0]);
}
return 0;
}
DWORD WINAPI ruiFu(void *p)
{//中介者不斷進行掃描等待執行緒通訊資訊
int i = 0;
int flag = 0;
while (++i)
{
if (!flag)
{//判斷通訊內容傳送方
WaitForSingleObject(eventArr[0], INFINITE);
EnterCriticalSection(&cs);
printf("媒婆傳遞%d次資訊%s \n", i, str);
LeaveCriticalSection(&cs);
Sleep(1000);
SetEvent(eventArr[1]);
flag = 1;//傳送方判定標識切換
}
else
{
WaitForSingleObject(eventArr[2], INFINITE);
EnterCriticalSection(&cs);
printf("媒婆傳遞%d次資訊%s \n", i, str);
LeaveCriticalSection(&cs);
Sleep(1000);
SetEvent(eventArr[3]);
flag = 0;
}
}
return 0;
}
DWORD WINAPI fangFang(void *p)
{
int i = 0;
while (++i)
{
WaitForSingleObject(eventArr[1], INFINITE);
EnterCriticalSection(&cs);
memset(str, '\0', 1024);
sprintf(str, "fangFang第%d次說:i love you too! \n", i);
LeaveCriticalSection(&cs);
Sleep(1000);
SetEvent(eventArr[2]);
}
return 0;
}
void main03()
{
//01.初始化臨界區結構體變數
InitializeCriticalSection(&cs);
//02.初始化事件控制程式碼陣列
eventArr[0] = CreateEvent(NULL, TRUE, FALSE, NULL);
eventArr[1] = CreateEvent(NULL, TRUE, FALSE, NULL);
eventArr[2] = CreateEvent(NULL, TRUE, FALSE, NULL);
eventArr[3] = CreateEvent(NULL, TRUE, FALSE, NULL);
//03.建立並執行多條非同步執行緒
threadArr[0] = CreateThread(NULL, 0, haiHua, NULL, 0, NULL);
threadArr[2] = CreateThread(NULL, 0, ruiFu, NULL, 0, NULL);
threadArr[1] = CreateThread(NULL, 0, fangFang, NULL, 0, NULL);
//04.等待除中介者之外其他的執行緒執行完畢之後,結束多執行緒狀態
WaitForMultipleObjects(2, threadArr, TRUE, INFINITE);
printf("over");
//05.釋放臨界區結構體變數:指標&作業系統排程
DeleteCriticalSection(&cs);
system("pause");
}
程式片段(08):執行緒衝突.c
內容概要:執行緒互相排斥
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
//01.全域性變數:表示多執行緒非同步併發訪問的通訊內容
// 極度容易導致多執行緒併發訪問同一內容的衝突性
int num = 0;
//02.互斥量:實質為(void *)型別的空型別地址變數
// 1.同一個互斥量最多能夠解決64個執行緒以內的多執行緒非同步併發訪問
// 所導致的衝突問題(這個限制僅僅只是針對於Windows作業系統)
// (針對於伺服器之上的多執行緒非同步併發訪問執行緒數沒有限制)
// 2.互斥量和臨界區的作用相同:
// 同是用於解決多執行緒非同步併發訪問所導致的衝突問題!
HANDLE mutex = NULL;
DWORD WINAPI add(void * p)
{
WaitForSingleObject(mutex, INFINITE);//新增互斥狀態
if (NULL == mutex)
{//新增互斥出錯
printf("新增執行緒互斥量失敗! \n");
abort();
}
for (int i = 0; i < 10000; ++i)
{
++num;
}
ReleaseMutex(mutex);//解除互斥狀態
return 0;
}
int main01(void)
{
mutex = CreateMutex(NULL, FALSE, NULL);
HANDLE threadArr[64] = { 0 };
for (int i = 0; i < 64; ++i)
{
threadArr[i] = CreateThread(NULL, 0, add, NULL, 0, NULL);
if (NULL == threadArr[i])
{
printf("執行緒建立失敗! \n");
}
}
WaitForMultipleObjects(64, threadArr, TRUE, INFINITE);
printf("num = %d \n", num);
for (int i = 0; i < 64; ++i)
{
CloseHandle(threadArr[i]);//關閉每條執行緒所佔用的資源
}
CloseHandle(mutex);//關閉互斥量所佔用的資源
system("pause");
}
//01.多執行緒互斥量問題:執行緒之間互相排斥
// 1.臨界區-->互斥量-->設計模式:佇列傳遞強化
// 2.互斥量:和臨界區一個作用,用於解決執行緒衝突問題
//注:臨界區解決程式碼段兒互斥+互斥量解決共享資源互斥訪問
//02.互斥量的缺點:
// 1.當我們所需操作的執行緒個數超過64個的時候就不行了
// 2.所有的執行緒都有數量限制,你在一定數量之下都處於正常情況,
// 一定數量之上就不會正常了[衝突臨界的執行緒數目限制]
//03.使用互斥量的步驟:
// 宣告互斥量:全域性變數
// HANDLE mutex = NULL;
// 初始化互斥量:
// mutex = CreateMutex(NULL,FALSE,NULL);
// 使用互斥量:
// 等待:WaitForSingleObject(mutex,INFINITE);//新增互斥狀態
// 釋放:ReleaseMutex(mutex);//解除互斥狀態
// 關閉互斥量:
// CloseHandle(mutex);
程式片段(09):01.Run.c+02.原子變數與原子操作.c
內容概要:原子變數與原子操作
///01.Run.c
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
//01.關於除錯(Debug)和發行(Release)狀態特點:
// Debug:不會進行程式碼自動優化操作
// Release:會進行程式碼自動優化操作
//02.關於register和volatile兩個關鍵字:
// register:將記憶體變數移植為暫存器變數(暫存器變數標識)
// volatile:防止記憶體變數移植為暫存器變數(記憶體變數標識)
//03.for(int i = 0; i < INT_MAX; ++i){}:
// 當前這段兒程式碼在編譯器Release發行模式之下,將會在
// 記憶體變數第一次移植到暫存器之後,不在頻繁讀寫記憶體變數
//原因:一直使用同一記憶體變數+迴圈執行體沒有操作該記憶體變數
int main01(void)
{
for (volatile int i = 0; i < INT_MAX; ++i) {}
printf("over");
system("pause");
}
//04.區分編譯器的優化與非優化情況:
// 1.早期編譯器和近期編譯器:
// 早期編譯器不會優化,近期編譯器會涉及優化
// 2.Debug模式和Release模式:
// Debug模式下會優化(操作暫存器),Release模式不會優化(操作記憶體)
//注:根據地址訪問資料相當於強制讀取記憶體操作(volatile操作)
volatile int num = 20;
DWORD WINAPI rmsg(void * p)
{//多執行緒讀取
//int * px = p;
int * px = (int *)p;
while (1)
{
//printf("%d \n", *px);//當心這兒的副本機制
//int data = *px;//根據地址讀取資料,相當於強制讀取記憶體
printf("%d \n", num);
Sleep(1000);
}
}
DWORD WINAPI wmsg(void * p)
{//多執行緒寫入
int * px = (int *)p;
while (1)
{
*px += 1;
Sleep(1000);
}
}
int main01(void)
{
CreateThread(NULL, 0, wmsg, &num, 0, NULL);
CreateThread(NULL, 0, wmsg, &num, 0, NULL);
CreateThread(NULL, 0, wmsg, &num, 0, NULL);
CreateThread(NULL, 0, rmsg, &num, 0, NULL);
system("pause");
}
//01.原子變數主要用於解決什麼樣兒的問題?
// 還是一樣的問題,多執行緒非同步併發訪問衝突問題(效率最高)
//02.三方互相通訊-->技術含量最高-->會使用到所有的多執行緒知識
// 只是:臨界區,互斥量,原子變數等等...
//03.原子變數:Atomitic
// 不可拆解的變數-->變數修改問題:兩個變數同時訪問一個
// 第三方變數,造成最終的訪問結果不正確(因此需要限定原子訪問)
//04.volatile:
// Debug模式:
// Release模式:程式碼優化作用,主要解決程式碼更快的效果
// 程式碼優化:讓你的程式碼更快,發現該變數在暫存器當中
// 沒有相應的操作,於是就直接讀取記憶體當中的資料
//05.Realese:實現無線等待的方式
//06.作業系統操作所有的變數都是依賴於暫存器的:
// 暫存器讀取副本-->強制讀取暫存器當中的資料
// 暫存器當中的原始副本-->與記憶體當中的普通副本
// 暫存器原始與副本[區分暫存器副本與記憶體副本]
//07.當指標指向一個變數之後,操作指標就等同於操作原本
// 注意副本機制
//08.不同編譯器的演示使用
// 多執行緒現代編譯器的優化特性
//09.暫存器變數與記憶體變數:
// register + volatile
//注:強制讀寫記憶體的情況之下等同於暫存器和記憶體狀態實時對應!
///02.原子變數與原子操作.c
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
//01.全域性變數:用於表示多執行緒非同步併發訪問之間的通訊資訊
// 1.對於全域性變數的訪問分為兩種(一種是寫入,一種是讀取):
// 針對於寫入操作,會發生多執行緒非同步併發訪問衝突問題
// 針對於讀取操作,不會發生多執行緒非同步併發訪問衝突 問題
// 2.解決多執行緒非同步執行併發訪問操作的衝突問題:
// 臨界區:用於程式碼段兒
// 互斥量:用於共享變數
// 原子量:用於單獨變數
//注:事件通知是用於執行緒之間的通訊,例如讓多條執行緒按照指定
// 的順序進行程式碼段兒的執行
//注:原子操作的速度要快於互斥量和臨界區的操作
// 1.因此,一般情況之下,如果多個執行緒同時訪問一個變數,就建議
// 採用原子變數
// 2.簡單的原理:
// int型別的變數操作-->其它型別的操作-->執行緒安全問題
int num = 0;
//02.對於當前這個執行緒任務程式碼函式的剖析:
// 1.這兒的++num翻譯為組合語言,以便於看清實質
// 2.翻譯為組合語言之後,就不只是一條執行語句了:
// (1).先將該記憶體變數載入進暫存器當中,形成暫存器變數
// (2).接著呼叫完成執行++num;操作
// (3).再將運算完成之後的結果返回到記憶體當中
// 3.因此++num;這條程式碼真實的執行操作其實是三條語句
// 體現了非原子性,因此容易出現多執行緒非同步併發訪問問題
// 4.由此,退出了原子變數的概念,相當於將這三個操作打包為
// 一個整體操作,多執行緒情況之下的同一時刻,只能由一條線
// 程進行訪問操作!
// 5.C和CPP包括遊戲裡面都提供了這種原子變數的操作機制:
// 原子變數的操作方法
//03.原子操作的方式分類:
// 1.分"型別"進行處理
// 2.分"寫入"和"讀取"進行處理
// 3.分"增加"和"減少"進行處理
// 4.分"原本"和"副本"進行分類
//注:這裡邊需要一個volatile型別的指標,用於強制讀寫記憶體
DWORD WINAPI runX(void * p)
{
for (int i = 0; i < 10000; ++i)
{
//++num;
//InterlockedIncrement(&num);//(++num) or (num++)
//InterlockedIncrement(&num, 1);//可指明遞增數
//InterlockedExchange(&num);//可修改性
//InterlocakedAdd(&num);//改變的是副本,原本不會發生改變
InterlocakedExchangeAdd(&num, 1);//原本會發生改變
}
}
int main01(void)
{
HANDLE threadArr[50] = { 0 };
for (int i = 0; i < 50; ++i)
{
threadArr[i] = CreateThread(NULL, 0, runX, NULL, 0, NULL);
}
WaitForMultipleObjects(50, threadArr, TRUE, INFINITE);
printf("num = %d \n", num);
system("pause");
}
//01.原子變數與原子操作(C++概念)
// 同樣用於解決執行緒安全問題
//02.原子變數主要解決什麼樣的問題?
// 多個執行緒併發訪問一個全域性變數常常都會發生一定的問題
// 所以需要進行解決(原子操作:不可分割的操作!)
//03.C語言當中的0和NULL在數值方面是完全等價的
// 只是一個具備地址層面的意義,一個不具有
//04.為了精確控制全域性變數的多執行緒併發訪問問題:
// 需要進行控制訪問特性
//5.什麼是執行緒安全?
// 純C編寫的-->C++編寫-->多執行緒必須
程式片段(10):main.c
內容概要:volatile
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <Windows.h>
int num = 1120;//現代編譯器:自動優化記憶體變數為暫存器變數
DWORD WINAPI rmsg(void *p)
{//讀取
int * px = (int *)p;
while (1)
{
//int data = *px;//等同於強制讀記憶體
printf("\n%d", num);
Sleep(1000);
}
}
DWORD WINAPI wmsg(void *p)
{//寫入
int * px = (int*)p;
while (1)
{
*px += 1;
Sleep(10000);
}
}
void main01()
{
CreateThread(NULL, 0, msg, &num, 0, NULL);
CreateThread(NULL, 0, msg, &num, 0, NULL);
CreateThread(NULL, 0, msg, &num, 0, NULL);
CreateThread(NULL, 0, cmsg, &num, 0, NULL);
system("pause");
}
程式片段(11):Time.c
內容概要:時間同步
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
//01.時間同步:定時器操作
// 1.由於作業系統自帶時間定時器,因此不用為程式獨立建立時間定時器:
// 利用已存在的資源,避免過多的佔用資源
// 2.定時器的實質還是一種事件通知機制:
// 在定時器到達了某一個指定刻度之後,發出某個事件通知
//注:需要檢測定時器的建立成功和失敗情況!
//02.定時器程式設計特點剖析:
// 1.定時器的精準性,精確到小數點兒之後的第7位
// 2.由於一個聯合體,因此能夠進行時間精準度的設定
//注:timer描述時間刻度非常精準
int main01(void)
{
HANDLE timer = CreateWaitableTimer(NULL, TRUE, NULL);
if (NULL == timer)
{
printf("定時器建立失敗! \n");
abort();
}
LARGE_INTEGER time;
//-5000 000 0 :毫秒-->微妙-->0.1微妙
time.QuadPart = -20000000;//延遲2秒啟動
//10^-7方秒-->等待功能
//設定定時器等待兩秒
SetWaitableTimer(timer, &time, 0, NULL, 0, NULL);//設定時間
//接收一個訊息
if (WAIT_OBJECT_0 == WaitForSingleObject(timer, INFINITE))
{
printf("等待成功! \n");
}
else
{
printf("等待失敗! \n");
}
while (1)
{
//01.單執行緒每隔兩秒做一件事情,沒機會做其他事情
// 這個叫做執行緒佔用
//02.通過作業系統進行執行緒的管理,時間進行指定
// 1.作業系統自動管理執行緒,通過時間進行檢測
// 2.時間同步是作業系統進行的管理
// 作業系統原理:每開啟一個視窗都一個地址
// (1)作業系統管理原理是通過連結串列進行管理
// (2)作業系統內部有一個死迴圈,不斷的進行檢測
// while(1)
// {讀到:鍵盤,滑鼠操作的資訊}
// 掃描資訊-->訊息傳給視窗
// 操作-->訊息[執行緒管理]-->本質操作
//03.時間統一的功能都交給了作業系統管理
// 所以我們這裡使用作業系統當中的時間同步原理
printf("fangfang \n");
Sleep(2000);
}
system("pause");
}
HANDLE timer;//(1.宣告定時器)
DWORD WINAPI go1(void *p)
{
MessageBoxA(0, "1", "1", 0);
//讓go1執行完畢之後5秒再執行go2
//(2.建立定時器)
timer = CreateWaitableTimer(NULL, TRUE, NULL);
LARGE_INTEGER time;
time.QuadPart = -50000000;//5秒
//10 -7秒 0.1微秒
SetWaitableTimer(timer, &time, 0, NULL, 0, NULL);//設定定時器等待 2聊
}
DWORD WINAPI go2(void *p)
{
//if (WaitForSingleObject(timer,INFINITE)==WAIT_OBJECT_0)
//{
WaitForSingleObject(timer, INFINITE);
MessageBoxA(0, "2", "2", 0);
printf("等待成功!");
//}
//else
//{
//printf("等待失敗!");
//}
}
void main02()
{
//通過主執行緒進行控制時間同步
HANDLE hd=CreateThread(NULL, 0, go1, NULL, 0, NULL);
//弊端:時間同步不能用於跨執行緒的同步
//單獨定時器:只能用於同步的通訊,不能用於非同步通訊
WaitForSingleObject(hd, INFINITE);
if (WaitForSingleObject(timer,INFINITE)==WAIT_OBJECT_0)
{
CreateThread(NULL, 0, go2, NULL, 0, NULL);
printf("等待成功!");
}
else
{
printf("等待失敗!");
}
getchar();
}
//01.根據時間進行同步:
// 原理:確定的時間趕往同一個地點
//02.時間同步:定時器原理[空間原理]
// 定時的完成一件事情
//03.定時器原理:
// 1.主執行緒獨立執行
// 2.時間執行緒:每隔3秒提供一個函式指標
// 3.接收到事情的時候就開始幹活兒
//04.時間鎖定
//05.時間同步可能只能執行於一個執行緒內部
程式片段(12):time.c
內容概要:多執行緒實戰
#include <stdio.h>
#include <stdlib.h>
#include <process.h>
#include <Windows.h>
int i = 1;//全域性變數:用於多執行緒通訊
void setTime(void * p)
{//定時器函式:
while (1)
{
Sleep(1000);
char str[40] = { 0 };
sprintf(str, "title 當前時間為第%3d的秒! \n", i++);
system(str);
}
}
void run(void * p)
{//主控制函式
while (1)
{
if (3 == i)
{
system("calc");
}
else if (10 == i)
{
system("notepad");
_endthread();
}
else if (19 == i)
{
system("tasklist & pause");
}
Sleep(1000);
}
}
int main01(void)
{
system("title China World! \n");
_beginthread(setTime, 0, NULL);//開啟定時器
_beginthread(run, 0, NULL);//主控制函式
system("pause");
}
程式片段(13):main.c
內容概要:多執行緒檢索陣列
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <process.h>
//01.採用多執行緒處理:
// 在陣列當中只存在一個待查詢資料的情況
int isFind = 0;//全域性變數-->多執行緒通訊內容
int * pFind = NULL;//查詢到的資料位置
typedef struct
{
int arrLen;
int findNum;
int thID;
int * intArr;
} thTaskInfo;
void searchNum(void * p)
{
thTaskInfo * pThTask = (thTaskInfo *)p;
printf("編號為%d的執行緒開始檢索: \n", pThTask->thID);
for (int * pTmp = pThTask->intArr; pTmp < pThTask->intArr + pThTask->arrLen; ++pTmp)
{
if (1 == isFind)
{
printf("編號為%d的執行緒結束檢索狀態,其它執行緒已檢索到指定資料! \n", pThTask->thID);
_endthread();
}
if (pThTask->findNum == *pTmp)
{
isFind = 1;
pFind = pTmp;
printf("編號為%d的執行緒已檢索到指定資料%d,該資料的位置為:%p ! \n", pThTask->thID, *pTmp, pTmp);
_endthread();
}
}
printf("編號為%d的執行緒未檢索到指定資料,結束檢索狀態! \n", pThTask->thID);
}
int main01(void)
{//理想情況之下
int data[1000] = { 0 };
for (int i = 999; i > -1; --i)
{
data[i] = i;
}
thTaskInfo thTaskArr[10] = { 0 };
for (int i = 0; i < 10; ++i)
{
thTaskArr[i].intArr = data + i * 100;
thTaskArr[i].arrLen = 100;
thTaskArr[i].findNum = 767;
thTaskArr[i].thID = i;
_beginthread(searchNum, 0, &thTaskArr[i]);
}
system("pause");
}
//02.多執行緒檢索無規律資料的規則:
// 1.採用N條執行緒執行檢索任務
// 2.其中(N-1)條執行緒所檢索的資料量一致
// 最後1條檢索的資料量不一致
// 3.其中(N-1)條執行緒所檢索的資料量單元
// 要大於最後一條執行緒所檢索的資料量單元
int main02(void)
{
int data[1000] = { 0 };
for (int i = 999; i > -1; --i)
{
data[i] = i;
}
int findNum = 0;
int thNum = 0;
scanf("%d %d", &findNum, &thNum);
int quotient = 1000 / thNum;
int remainder = 1000 % thNum;
thTaskInfo * pThTaskArr = (thTaskInfo *)malloc(thNum *sizeof(thTaskInfo));
if (!remainder)
{
for (int i = 0; i < thNum; ++i)
{
(pThTaskArr + i)->intArr = data + i * (1000 / thNum);
(pThTaskArr + i)->arrLen = 1000 / thNum;
(pThTaskArr + i)->findNum = findNum;
(pThTaskArr + i)->thID = i;
_beginthread(searchNum, 0, pThTaskArr + i);
}
}
else
{
for (int i = 0; i < thNum - 1; ++i)
{
(pThTaskArr + i)->intArr = data + i * (1000 / quotient);
(pThTaskArr + i)->arrLen = 1000 / quotient;
(pThTaskArr + i)->findNum = findNum;
(pThTaskArr + i)->thID = i;
_beginthread(searchNum, 0, pThTaskArr + i);
}
int endI = thNum - 1;
(pThTaskArr + endI)->intArr = data + quotient * (thNum - 1);
(pThTaskArr + endI)->arrLen = remainder;
(pThTaskArr + endI)->findNum = findNum;
(pThTaskArr + endI)->thID = endI;
_beginthread(searchNum, 0, pThTaskArr + endI);
}
system("pause");
}
程式片段(14):DualCircle.h+DualCircle.c
內容概要:雙環
///DualCircle.h
#pragma once
typedef struct node
{
int data;
struct node * pPre;
struct node * pNext;
}Node;
typedef struct dualcircle
{
Node * pHead;
Node * pTail;
}DualCircle;
void initNodeWithData(Node * pNode, int data);
void initDualCircle(DualCircle * pDualCircle);
void dualCircleHeadInsert(DualCircle * pDualCircle, int data);
void dualCircleTailInsert(DualCircle * pDualCircle, int data);
Node * dualCircleSelectFirst(DualCircle * pDualCircle, int data);
void dualCircleRandInsert(DualCircle * pDualCircle, int findData, int insertData);
void showDualCircle(DualCircle * pDualCircle);
void dualCircleDeleteFirst(DualCircle * pDualCircle, int data);
void dualCircleUpdateFirst(DualCircle * pDualCircle, int oldData, int newData);
///DualCircle.c
#include "DualCircle.h"
#include <Windows.h>
void initNodeWithData(Node * pNode, int data)
{
if (NULL == pNode)
abort();
pNode->data = data;
pNode->pPre = NULL;
pNode->pNext = NULL;
}
void initDualCircle(DualCircle * pDualCircle)
{
if (NULL == pDualCircle)
abort();
pDualCircle->pHead = pDualCircle->pTail = NULL;
}
void dualCircleHeadInsert(DualCircle * pDualCircle, int data)
{
if (NULL == pDualCircle)
abort();
Node * pNew = (Node *)malloc(sizeof(Node));
initNodeWithData(pNew, data);
if (NULL == pDualCircle->pHead)
{//空雙環
pNew->pPre = pNew->pNext = pNew;
pDualCircle->pHead = pDualCircle->pTail = pNew;
return;
}
if (pDualCircle->pHead == pDualCircle->pTail)
{//單節點
pNew->pPre = pNew->pNext = pDualCircle->pHead;
pDualCircle->pTail->pPre = pDualCircle->pTail->pNext = pNew;
pDualCircle->pHead = pNew;
return;
}//多節點
pNew->pPre = pDualCircle->pTail;
pNew->pNext = pDualCircle->pHead;
pDualCircle->pHead = pNew;
pDualCircle->pTail->pNext = pNew;
}
void dualCircleTailInsert(DualCircle * pDualCircle, int data){}
Node * dualCircleSelectFirst(DualCircle * pDualCircle, int data)
{
if (NULL == pDualCircle)
abort();
if (NULL == pDualCircle->pHead)
abort();
Node * pTmp = pDualCircle->pHead;
do
{
if (data == pTmp->data)
return pTmp;
pTmp = pTmp->pNext;
} while (pDualCircle->pHead != pTmp);
return NULL;
}
void dualCircleRandInsert(DualCircle * pDualCircle, int findData, int insertData)
{
if (NULL == pDualCircle)
abort();
if (NULL == pDualCircle->pHead)
abort();
int find = 0;
Node * pTmp = pDualCircle->pHead;
do
{
if (findData == pTmp->data)
{
find = 1;
break;
}
pTmp = pTmp->pNext;
} while (pDualCircle->pHead != pTmp);
if (!find)
return;
Node * pInsert = (Node *)malloc(sizeof(Node));
initNodeWithData(pInsert, insertData);
if (pDualCircle->pTail == pTmp)
{
pDualCircle->pTail->pNext = pInsert;
pInsert->pPre = pDualCircle->pTail;
pInsert->pNext = pDualCircle->pHead;
pDualCircle->pHead->pPre = pInsert;
pDualCircle->pTail = pInsert;
return;
}
pInsert->pPre = pTmp;
pInsert->pNext = pTmp->pNext;
pTmp->pNext = pInsert;
pTmp->pNext->pPre = pInsert;
}
//其它的沒有寫頭,自己去想吧!
相關文章
- 20160217.CCPP體系詳解(0027天)
- 20160124.CCPP詳解體系(0003天)
- 20160125.CCPP詳解體系(0004天)
- 20160126.CCPP體系詳解(0005天)
- 20160127.CCPP體系詳解(0006天)
- 20160130.CCPP體系詳解(0009天)
- 20160203.CCPP體系詳解(0013天)
- 20160211.CCPP體系詳解(0021天)
- 20160213.CCPP體系詳解(0023天)
- 20160214.CCPP體系詳解(0024天)
- 20160215.CCPP體系詳解(0025天)
- 20160224.CCPP體系詳解(0034天)
- 20160218.CCPP體系詳解(0028天)
- 20160219.CCPP體系詳解(0029天)
- 手遊《天地劫》的三天體驗——深度系統剖析及玩法詳解
- 20160122.CCPP詳解體系(0001天)
- 20160123.CCPP詳解體系(0002天)
- 20160128.CCPP體系詳解(0007天)
- 20160129.CCPP體系詳解(0008天)
- 20160131.CCPP體系詳解(0010天)
- 20160204.CCPP體系詳解(0014天)
- 20160205.CCPP體系詳解(0015天)
- 20160210.CCPP體系詳解(0020天)
- 20160212.CCPP體系詳解(0022天)
- 20160207.CCPP體系詳解(0017天)
- 20160225.CCPP體系詳解(0035天)
- 20160227.CCPP體系詳解(0037天)
- 20160222.CCPP體系詳解(0032天)
- 20160221.CCPP體系詳解(0031天)
- 20160201.CCPP體系詳解(0011天)
- 20160202.CCPP體系詳解(0012天)
- 20160209.CCPP體系詳解(0019天)
- 20160216.CCPP體系詳解(0026天)
- 20160206.CCPP體系詳解(0016天)
- 20160208.CCPP體系詳解(0018天)
- 20160223.CCPP體系詳解(0033天)
- 20160220.CCPP體系詳解(0030天)
- MySQL體系結構詳解MySql