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


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

假定工廠的電力有限,一次只能供給一個或少量幾個車間使用。也就是說,一部分車間開工的時候,其他車間都必須停工。背后的含義就是,單個CPU一次只能運行一個任務,多個CPU能夠運行少量任務。


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

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

三、創(chuàng)建線程函數
1.CreateThread
CreateThread是一種微軟在Windows API中提供了建立新的線程的函數,該函數在主線程的基礎上創(chuàng)建一個新線程。線程終止運行后,線程對象仍然在系統(tǒng)中,必須通過CloseHandle函數來關閉該線程對象。
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,//SD
SIZE_T dwStackSize,//initialstacksize
LPTHREAD_START_ROUTINE lpStartAddress,//threadfunction
LPVOID lpParameter,//threadargument
DWORD dwCreationFlags,//creationoption
LPDWORD lpThreadId//threadidentifier
)第一個參數 lpThreadAttributes 表示線程內核對象的安全屬性,一般傳入NULL表示使用默認設置。
第二個參數 dwStackSize 表示線程??臻g大小。傳入0表示使用默認大?。?MB)。
第三個參數 lpStartAddress 表示新線程所執(zhí)行的線程函數地址,多個線程可以使用同一個函數地址。
第四個參數 lpParameter 是傳給線程函數的參數。
第五個參數 dwCreationFlags 指定額外的標志來控制線程的創(chuàng)建,為0表示線程創(chuàng)建之后立即就可以進行調度,如果為CREATE_SUSPENDED則表示線程創(chuàng)建后暫停運行,這樣它就無法調度,直到調用ResumeThread()。
第六個參數 lpThreadId 將返回線程的ID號,傳入NULL表示不需要返回該線程ID號
2._beginthreadex
unsigned long _beginthreadex(
void *security, // 安全屬性, 為NULL時表示默認安全性
unsigned stack_size, // 線程的堆棧大小, 一般默認為0
unsigned(_stdcall *start_address)(void *), // 線程函數
void *argilist, // 線程函數的參數
unsigned initflag, // 新線程的初始狀態(tài),0表示立即執(zhí)行,//CREATE_SUSPENDED表示創(chuàng)建之后掛起
unsigned *threaddr // 用來接收線程ID
);返回值 : // 成功返回新線程句柄, 失敗返回0
__stdcall表示
1.參數從右向左壓入堆棧
2.函數被調用者修改堆棧
四、簡單多線程示例
現在有三個任務,Tom每隔3秒捉一次Jerry,Jerry每隔2秒吃一次奶酪,Spike每隔1秒打一次Tom。分別用CreateThread和_beginthreadex實現
使用_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;
}運行結果:

使用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);
//關閉線程
if (hThead) {
CloseHandle(hThead);
}
system("pause");
return 0;
}運行結果:

到此這篇關于C/C++中線程基本概念與創(chuàng)建詳解的文章就介紹到這了,更多相關C++線程內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
C++時間戳轉化操作實例分析【涉及GMT與CST時區(qū)轉化】
這篇文章主要介紹了C++時間戳轉化操作,結合實例形式分析了C++時間戳轉換與顯示操作的原理與具體實現技巧,涉及GMT與CST時區(qū)轉化,需要的朋友可以參考下2017-05-05

