C/C++中線程基本概念與創(chuàng)建詳解
一、線程基本概念
線程是在進(jìn)程中產(chǎn)生的一個(gè)執(zhí)行單元,是CPU調(diào)度和分配的最小單元,其在同一個(gè)進(jìn)程中與其他線程并行運(yùn)行,他們可以共享進(jìn)程內(nèi)的資源,比如內(nèi)存、地址空間、打開(kāi)的文件等等。
線程是CPU調(diào)度和分派的基本單位,
進(jìn)程是分配資源的基本單位
進(jìn)程:正在運(yùn)行的程序
是處于執(zhí)行期的程序以及它所管理的資源(如打開(kāi)的文件、掛起的信號(hào)、進(jìn)程狀態(tài)、地址空間等等)的總稱,從操作系統(tǒng)核心角度來(lái)說(shuō),進(jìn)程是操作系統(tǒng)調(diào)度除CPU時(shí)間片外進(jìn)行的資源分配和保護(hù)的基本單位,它有一個(gè)獨(dú)立的虛擬地址空間,用來(lái)容納進(jìn)程映像(如與進(jìn)程關(guān)聯(lián)的程序與數(shù)據(jù)),并以進(jìn)程為單位對(duì)各種資源實(shí)施保護(hù),如受保護(hù)地訪問(wèn)處理器、文件、外部設(shè)備及其他進(jìn)程(進(jìn)程間通信)


計(jì)算機(jī)有很多資源組成,比如CPU、內(nèi)存、磁盤、鼠標(biāo)、鍵盤等,就像一個(gè)工廠由電力系統(tǒng)、作業(yè)車間、倉(cāng)庫(kù)、管理辦公室和工人組成

假定工廠的電力有限,一次只能供給一個(gè)或少量幾個(gè)車間使用。也就是說(shuō),一部分車間開(kāi)工的時(shí)候,其他車間都必須停工。背后的含義就是,單個(gè)CPU一次只能運(yùn)行一個(gè)任務(wù),多個(gè)CPU能夠運(yùn)行少量任務(wù)。


線程就好比車間里的工人。一個(gè)進(jìn)程可以包括多個(gè)線程,他們協(xié)同完成某一個(gè)任務(wù)。

二、為什么使用多線程
1.避免阻塞
大家知道,單個(gè)進(jìn)程只有一個(gè)主線程,當(dāng)主線程阻塞的時(shí)候,整個(gè)進(jìn)程也就阻塞了,無(wú)法再去做其它的一些功能了。
2.避免CPU空轉(zhuǎn)
應(yīng)用程序經(jīng)常會(huì)涉及到RPC,數(shù)據(jù)庫(kù)訪問(wèn),磁盤IO等操作,這些操作的速度比CPU慢很多,而在等待這些響應(yīng)時(shí),CPU卻不能去處理新的請(qǐng)求,導(dǎo)致這種單線程的應(yīng)用程序性能很差。
3.提升效率
一個(gè)進(jìn)程要獨(dú)立擁有4GB的虛擬地址空間,而多個(gè)線程可以共享同一地址空間,線程的切換比進(jìn)程的切換要快得多。
上下文切換

三、創(chuàng)建線程函數(shù)
1.CreateThread
CreateThread是一種微軟在Windows API中提供了建立新的線程的函數(shù),該函數(shù)在主線程的基礎(chǔ)上創(chuàng)建一個(gè)新線程。線程終止運(yùn)行后,線程對(duì)象仍然在系統(tǒng)中,必須通過(guò)CloseHandle函數(shù)來(lái)關(guān)閉該線程對(duì)象。
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,//SD
SIZE_T dwStackSize,//initialstacksize
LPTHREAD_START_ROUTINE lpStartAddress,//threadfunction
LPVOID lpParameter,//threadargument
DWORD dwCreationFlags,//creationoption
LPDWORD lpThreadId//threadidentifier
)第一個(gè)參數(shù) lpThreadAttributes 表示線程內(nèi)核對(duì)象的安全屬性,一般傳入NULL表示使用默認(rèn)設(shè)置。
第二個(gè)參數(shù) dwStackSize 表示線程??臻g大小。傳入0表示使用默認(rèn)大小(1MB)。
第三個(gè)參數(shù) lpStartAddress 表示新線程所執(zhí)行的線程函數(shù)地址,多個(gè)線程可以使用同一個(gè)函數(shù)地址。
第四個(gè)參數(shù) lpParameter 是傳給線程函數(shù)的參數(shù)。
第五個(gè)參數(shù) dwCreationFlags 指定額外的標(biāo)志來(lái)控制線程的創(chuàng)建,為0表示線程創(chuàng)建之后立即就可以進(jìn)行調(diào)度,如果為CREATE_SUSPENDED則表示線程創(chuàng)建后暫停運(yùn)行,這樣它就無(wú)法調(diào)度,直到調(diào)用ResumeThread()。
第六個(gè)參數(shù) lpThreadId 將返回線程的ID號(hào),傳入NULL表示不需要返回該線程ID號(hào)
2._beginthreadex
unsigned long _beginthreadex(
void *security, // 安全屬性, 為NULL時(shí)表示默認(rèn)安全性
unsigned stack_size, // 線程的堆棧大小, 一般默認(rèn)為0
unsigned(_stdcall *start_address)(void *), // 線程函數(shù)
void *argilist, // 線程函數(shù)的參數(shù)
unsigned initflag, // 新線程的初始狀態(tài),0表示立即執(zhí)行,//CREATE_SUSPENDED表示創(chuàng)建之后掛起
unsigned *threaddr // 用來(lái)接收線程ID
);返回值 : // 成功返回新線程句柄, 失敗返回0
__stdcall表示
1.參數(shù)從右向左壓入堆棧
2.函數(shù)被調(diào)用者修改堆棧
四、簡(jiǎn)單多線程示例
現(xiàn)在有三個(gè)任務(wù),Tom每隔3秒捉一次Jerry,Jerry每隔2秒吃一次奶酪,Spike每隔1秒打一次Tom。分別用CreateThread和_beginthreadex實(shí)現(xiàn)
使用_beginthreadex
#include<stdio.h>
#include<Windows.h>
#include<process.h>
//Tom每隔3秒捉一次老鼠
unsigned WINAPI thread_main_Tom(void* arg) {
int cnt = *((int*)arg);
for (int i = 0; i < cnt; i++) {
Sleep(3000);
puts("Tom 捉老鼠\n");
}
return 0;
}
//Jerry每隔1秒吃一次奶酪
unsigned WINAPI thread_main_Jerry(void* arg) {
int cnt = *((int*)arg);
for (int i = 0; i < cnt; i++) {
Sleep(1000);
puts("Jerry 吃奶酪\n");
}
return 0;
}
//Spike每隔2秒打一次貓
unsigned WINAPI thread_main_Spike(void* arg) {
int cnt = *((int*)arg);
for (int i = 0; i < cnt; i++) {
Sleep(2000);
puts("Spike 打貓\n");
}
return 0;
}
int main() {
int Tom = 20, Jerry = 50, Spike = 40;
//保存線程Id
unsigned int Tom_id, Jerry_id, Spike_id;
//創(chuàng)建線程
_beginthreadex(NULL, 0, thread_main_Tom, (void*)&Tom, 0, &Tom_id);
_beginthreadex(NULL, 0, thread_main_Jerry, (void*)&Jerry, 0, &Jerry_id);
_beginthreadex(NULL, 0, thread_main_Spike, (void*)&Spike, 0, &Spike_id);
system("pause");
return 0;
}運(yùn)行結(jié)果:

使用CreateThread
#include<stdio.h>
#include<Windows.h>
#include<process.h>
//DWORD就是unsigned long
//LPVOID是void*
DWORD _stdcall ThreadFun(LPVOID p) {
printf("我是子線程,PID=%d", GetCurrentThreadId());
return 0;
}
int main() {
printf("main begin\n");
HANDLE hThead;
DWORD dwThreadID;
hThead = CreateThread(NULL, 0, ThreadFun, 0, 0, &dwThreadID);
printf("我是主線程,PID=%d\n",GetCurrentThreadId());
Sleep(2000);
//關(guān)閉線程
if (hThead) {
CloseHandle(hThead);
}
system("pause");
return 0;
}運(yùn)行結(jié)果:

到此這篇關(guān)于C/C++中線程基本概念與創(chuàng)建詳解的文章就介紹到這了,更多相關(guān)C++線程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++實(shí)現(xiàn)LeetCode(166.分?jǐn)?shù)轉(zhuǎn)循環(huán)小數(shù))
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(166.分?jǐn)?shù)轉(zhuǎn)循環(huán)小數(shù))2021-07-07
C++設(shè)計(jì)模式之迭代器模式(Iterator)
這篇文章主要為大家詳細(xì)介紹了C++設(shè)計(jì)模式之迭代器模式Iterator,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-04-04
C語(yǔ)言 完整游戲項(xiàng)目推箱子詳細(xì)代碼
經(jīng)典的推箱子是一個(gè)的古老游戲,目的是在訓(xùn)練你的邏輯思考能力。在一個(gè)狹小的倉(cāng)庫(kù)中,要求把木箱放到指定的位置,稍不小心就會(huì)出現(xiàn)箱子無(wú)法移動(dòng)或者通道被堵住的情況,所以需要巧妙的利用有限的空間和通道,合理安排移動(dòng)的次序和位置,才能順利的完成任務(wù)2021-11-11
Qt5開(kāi)發(fā)視頻播放器的項(xiàng)目實(shí)踐
Qt對(duì)音視頻的播放和控制、相機(jī)拍攝、收音機(jī)等多媒體應(yīng)用提供了強(qiáng)大的支持,本文主要介紹了Qt5開(kāi)發(fā)視頻播放器,具有一定的參考價(jià)值,感興趣的可以了解一下2023-08-08
總結(jié)了24個(gè)C++的大坑,你能躲過(guò)幾個(gè)
這篇文章主要介紹了總結(jié)了24個(gè)C++的大坑,你能躲過(guò)幾個(gè),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2021-05-05
C語(yǔ)言實(shí)現(xiàn)BMP圖像處理(彩色圖轉(zhuǎn)灰度圖)
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)BMP圖像處理,彩色圖轉(zhuǎn)灰度圖,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-10-10

