C++后端面试题目
C++后端面試知識
- 1. 師兄建議
- 2. 內(nèi)存泄漏
- 怎么避免
- 怎么檢測
- 內(nèi)存溢出
- 3. 堆和棧
- 4. static
- 5. const
- 6. URL
- 7. C和C++區(qū)別
- 8. malloc和new區(qū)別
- 9. 指針和引用區(qū)別
- 10.C++中內(nèi)存分區(qū)
- 11. 野指針
- 12. 類的默認函數(shù)
- 13. class和struct區(qū)別
- 14. 構(gòu)造函數(shù)中初始值列表的必要性
- 15. 虛函數(shù)
- 16. 繼承中的訪問權(quán)限
- 17. C++防止頭文件重復包含
- 18.TCP連接和斷開
- 19. 長連接和短連接
- 20. 進程和線程區(qū)別
- 21. 進程通信和線程通信
- 22. 線程池
- 23. 實踐中優(yōu)化MySQL
- 24. 設計高并發(fā)的系統(tǒng)
- 25. 小端和大端機器
- 26. strlen和sizeof區(qū)別
本文多數(shù)總結(jié)來自于CSDN博主「Tracker-for-1995」的文章【C++后端面試知識精選系列】,鏈接為:https://me.csdn.net/trackxiaoxin321。若有錯誤,望指正,筆者的郵箱為:wuxiaofang555555@163.com 。
1. 師兄建議
- 國企看重簡歷中的硬性指標,如:論文情況、比賽、專利、軟著等
- Qt
- C++基礎知識(較瑣碎)
- 類
- 面向?qū)ο缶幊痰奶攸c:抽象、封裝、繼承、多態(tài)
- 引用的用法
- 指針的用法
- 內(nèi)存泄漏
- 內(nèi)存溢出
2. 內(nèi)存泄漏
指程序中己動態(tài)分配的堆內(nèi)存由于某種原因程序未釋放或無法釋放,造成系統(tǒng)內(nèi)存的浪費,導致程序運行速度減慢甚至系統(tǒng)崩潰等嚴重后果。
當程序員使用new或是malloc分配內(nèi)存,而忘記使用delete或是free釋放內(nèi)存時,容易造成內(nèi)存泄漏。
怎么避免
- 使用智能指針,而不是手動地去管理內(nèi)存
- 程序進程字符串處理時,避免使用string代替char*
- 使用RAII(Resource Acquisition Is Initialization,資源獲取就是初始化),RAII 的做法是使用一個對象,在其構(gòu)造時獲取資源,最后在對象析構(gòu)的時候釋放資源。即把資源用類進行封裝起來,對資源操作都封裝在類的內(nèi)部,在析構(gòu)函數(shù)中進行釋放資源。
- 盡可能少的使用new,若一定要使用,new/malloc分配內(nèi)存,要及時delete/free釋放
怎么檢測
-
new和delete都是內(nèi)置的操作符
void * operator new(size_t size); //new 操作符的聲明 //返回一個未初始化的地址,其中size來確定分配多少內(nèi)存 -
自定義跟蹤new和delete的類
-
重載new和delete操作符,通過計數(shù)new的次數(shù)和delete次數(shù),即可判斷是否存在內(nèi)存泄漏
-
track_new.h
#ifndef TRACK_NEW_H #define TRACK_NEW_H #include <map> void* operator new(size_t size, const char *file, long line); void* operator new(size_t size); void operator delete(void *p); class TrackerNew {class TrackerNewIn {const char *m_file;long m_line;public:TrackerNewIn(const char *file = nullptr, long line = 0) :m_file(file), m_line(line){ }~TrackerNewIn(){ }const char *file() const { return m_file; }long line() const { return m_line; }};class Lock { //省去m_lock_cout的自減,避免出錯TrackerNew &_track;public:Lock(TrackerNew &track) :_track(track) {_track.m_lock_count++;}~Lock() {_track.m_lock_count--;}}; public:TrackerNew();~TrackerNew();void add(void *p, const char *file, long line);void remove(void *p);void dump(); public:static bool flag; private:std::map<void*, TrackerNewIn> m_track;long m_lock_count; //為了避免無限調(diào)用 }; extern TrackerNew tracker_new; #endif TRACK_NEW_H //!TRACK_NEW_H -
track_new.cpp
#include"track_new.h" #include <cstdlib> #include <iostream> TrackerNew tracker_new;//定義全局的類 bool TrackerNew::flag = false; void* operator new(size_t size, const char *file, long line) {void *p = malloc(size);if (TrackerNew::flag)tracker_new.add(p, file, line);return p; } void* operator new(size_t size) {void *p = malloc(size);if (TrackerNew::flag)tracker_new.add(p, "unknow", -1);return p; } void operator delete(void *p) {if (TrackerNew::flag)tracker_new.remove(p);free(p); } TrackerNew::TrackerNew() :m_lock_count(0) {TrackerNew::flag = true; } TrackerNew::~TrackerNew() {TrackerNew::flag = false;dump(); } void TrackerNew::add(void *p, const char *file, long line) {if (m_lock_count > 0) return;Lock lock(*this);m_track[p] = TrackerNewIn(file, line);/*m_lock_count++;m_track[p] = TrackerNewIn(file, line);m_lock_count--;*/ } void TrackerNew::remove(void *p) {if (m_lock_count > 0)return;Lock lock(*this);auto it = m_track.find(p);if (it != m_track.end()) {m_track.erase(it);} } void TrackerNew::dump() {for (auto it : m_track) {std::cout << "ox: " << it.first << "\t" << "file: " << it.second.file() << "\t" << "line: " << it.second.line() << std::endl;} } -
main.cpp
#include "track_new.h" #include "debug_new.h" int main() {int *p1 = new int;delete p1;int *p2 = new int[10];//delete[] p2; return 0; }
-
內(nèi)存溢出
-
指應用系統(tǒng)中存在無法回收的內(nèi)存或使用的內(nèi)存過多,最終使得程序運行要用到的內(nèi)存大于能提供的最大內(nèi)存
-
主要發(fā)生在棧內(nèi)存溢出
-
遞歸調(diào)用中,若調(diào)用次數(shù)過多,易導致棧溢出
3. 堆和棧
-
數(shù)據(jù)結(jié)構(gòu)層面
- 棧:后進先出
- 堆:二叉堆,樹型結(jié)構(gòu)、可任意訪問
-
內(nèi)存分配層面(以地址的增長方向為上)
- 棧區(qū):處于相對較高地址,棧地址是向下增長的;棧中分配局部變量空間。
- 堆區(qū):堆區(qū)是向上增長的用于分配程序員申請的內(nèi)存空間,例如使用malloc、new申請的區(qū)域。
-
區(qū)別總結(jié)一
-
堆和棧申請方式不同
- 棧是系統(tǒng)自動分配空間,如定義一個char a;系統(tǒng)會自動在棧上為其開辟空間。棧上的數(shù)據(jù)的生存周期只是在函數(shù)的運行過程中,運行后就釋放掉了,不能再訪問了。
- 堆則是程序員根據(jù)需要自己申請的空間,如malloc(10);開辟十個字節(jié)的空間。堆上的數(shù)據(jù)只要程序員不釋放空間,就一直可以訪問,一旦頑疾釋放會造成內(nèi)存泄漏。
-
-
申請后系統(tǒng)的響應
-
棧:只要棧的剩余空間大于所申請空間,系統(tǒng)將為程序提供內(nèi)存,否則將報異常提示棧溢出。
- 堆:首先應該知道操作系統(tǒng)有一個記錄空閑內(nèi)存地址的鏈表,當系統(tǒng)收到程序的申請時,會遍歷該鏈表,尋找第一個空間大于所申請空間的堆。結(jié)點,然后將該結(jié)點從空閑結(jié)點鏈表中刪除,并將該結(jié)點的空間分配給程序,另外,對于大多數(shù)系統(tǒng),會在這塊內(nèi)存空間中的首地址處記錄本次分配的大小,這樣,代碼中的delete語句才能正確的釋放本內(nèi)存空間。另外,由于找到的堆結(jié)點的大小不一定正好等于申請的大小,系統(tǒng)會自動的將多余的那部分重新放入空閑鏈表中。也就是說堆會在申請后還要做一些后續(xù)的工作這就會引出申請效率的問題。
-
申請效率的比較
- 由系統(tǒng)自動分配,速度較快。但程序員是無法控制的。
-
是由new分配的內(nèi)存,一般速度比較慢,而且容易產(chǎn)生內(nèi)存碎片,不過用起來最方便
-
申請大小的限制
-
棧是向低地址擴展的數(shù)據(jù)結(jié)構(gòu),是一塊連續(xù)的內(nèi)存的區(qū)域。意思是棧頂?shù)牡刂泛蜅5淖畲笕萘渴窍到y(tǒng)預先規(guī)定好的。因此,能從棧獲得的空間較小。
- 堆是向高地址擴展的數(shù)據(jù)結(jié)構(gòu),是不連續(xù)的內(nèi)存區(qū)域。堆獲得的空間比較靈活,也比較大。
-
-
堆和棧中的存儲內(nèi)容
- 棧: 在函數(shù)調(diào)用時,第一個進棧的是主函數(shù)中函數(shù)調(diào)用后的下一條指令(函數(shù)調(diào)用語句的下一條可執(zhí)行語句)的地址,然后是函數(shù)的各個參數(shù),在大多數(shù)的C編譯器中,參數(shù)是由右往左入棧的,然后是函數(shù)中的局部變量。注意靜態(tài)變量是不入棧的。當本次函數(shù)調(diào)用結(jié)束后,局部變量先出棧,然后是參數(shù),最后棧頂指針指向最開始存的地址,也就是主函數(shù)中的下一條指令,程序由該點繼續(xù)運行。
- 堆:一般是在堆的頭部用一個字節(jié)存放堆的大小。堆中的具體內(nèi)容有程序員安排。
- 存取效率的比較:存取中,棧上的數(shù)據(jù)比堆上的數(shù)據(jù)快。
-
區(qū)別總結(jié)二
- 存儲內(nèi)容:棧存放的是局部變量和函數(shù)參數(shù)等;堆存放的是new/malloc申請的變量;
- 管理方式:棧由系統(tǒng)自動分配,自動釋放;堆由程序員手動申請,手動釋放;
- 空間大小:32位系統(tǒng)一般堆能達到4G,可以看做堆沒有什么限制的,但棧一般是有特定大小的,windows下一般2M;
- 生長方向:棧是向下(高地址——>低地址)生長的,堆是向上生長(低地址——>高地址)的;
- 申請效率:棧由系統(tǒng)自動申請分配,速度較快;堆由new/malloc分配,速度較慢。
4. static
- 修飾全局變量
- 修飾全局變量,不初始化默認初始為0,存儲在靜態(tài)存儲區(qū),從聲明到程序結(jié)束一直存在
- 修飾局部變量
- 也存儲在靜態(tài)存儲區(qū),作用域是局部作用域,當定義他的函數(shù)或者語句塊結(jié)束時,作用域結(jié)束,但該變量并沒有被銷毀,只不過我們不能訪問,直到該函數(shù)再次被調(diào)用
- 修飾類的數(shù)據(jù)成員
- 不屬于類的對象,類內(nèi)聲明,類外初始化,類的靜態(tài)成員變量被所有對象共享,保證了共享性,又具備安全性
- 修飾類的成員函數(shù)
- 靜態(tài)成員函數(shù)只能訪問靜態(tài)成員變量,不能訪問非靜態(tài)成員函數(shù),原因是靜態(tài)成員函數(shù)不與任何對象綁定,因此不包含this指針,在靜態(tài)成員函數(shù)內(nèi)部不能直接使用this指針,因此不能訪問類的非靜態(tài)成員變量
- 修飾函數(shù)
- 即靜態(tài)函數(shù),僅在聲明它的cpp文件中使用,其他文件不可使用
- 面試例子
- static關(guān)鍵字的作用,修飾函數(shù)有什么用?
- static修飾的函數(shù)叫靜態(tài)函數(shù),包括兩種
- 靜態(tài)函數(shù)出現(xiàn)在類里,則它是一個靜態(tài)成員函數(shù)。靜態(tài)成員函數(shù)不與任何對象綁定,因此不包含this指針,在靜態(tài)成員函數(shù)內(nèi)部不能直接使用this指針,即不能直接訪問普通成員函數(shù);靜態(tài)成員函數(shù)屬于類本身,在類加載時就會分配內(nèi)存,可以通過類名直接去訪問;雖然靜態(tài)成員函數(shù)不屬于類對象,但是可以通過類對象訪問靜態(tài)成員函數(shù)。
- 普通的全局的靜態(tài)函數(shù)。限定在本源碼文件中,不能在其他文件調(diào)用(普通函數(shù)的定義和聲明默認情況下都是extern的,即可被其他文件調(diào)用)。故好處在于:1)其他文件中可以定義相同名字的函
- static修飾的函數(shù)叫靜態(tài)函數(shù),包括兩種
- static關(guān)鍵字的作用,修飾函數(shù)有什么用?
5. const
-
修飾變量,使其不能被修改
-
修飾函數(shù)參數(shù),表明輸入的參數(shù)在參數(shù)內(nèi)不能被修改
-
修飾指針
- 常量指針,指向的內(nèi)容不能被修改,即指向“常量”的指針 , const int *p
- 指針常量,指針的指向不能改變,即指針類型的常量,int * const p
-
修飾類的成員函數(shù),表明其是常函數(shù),即不能修改類的成員變量,const成員函數(shù)斌調(diào)用非const成員函數(shù),因為非const成員函數(shù)可能會修改成員變量
class A {int m_a; public:void get() const { } //函數(shù)體里不能修改成員變量 }
6. URL
- 瀏覽器輸入www.baidu.com發(fā)生了什么?(百度面試題)
- 首先瀏覽器向域名系統(tǒng)DNS請求將域名地址轉(zhuǎn)換為IP地址,然后與百度服務器建立TCP連接,發(fā)送http請求(默認端口80)請求百度首頁,服務器處理請求并將首頁文件發(fā)給瀏覽器,TCP連接釋放,瀏覽器解析首頁文件展示web界面。
7. C和C++區(qū)別
- C是面向過程的編程語言,C++是面向?qū)ο蟮慕Y(jié)構(gòu)化編程語言,C++是C的擴展,但C和C++確是兩種不同的語言;
- C++有抽象、封裝、繼承、多態(tài)幾大特性,C+相比于C,增加了許多類型安全的功能,同時還有功能強大STL標準庫;
- 作用域,C中只有局部和全局,C++有局部、類作用域和名稱作用域三種;
- 開辟/釋放空間,C用malloc和free(函數(shù)),C++使用new和delete(關(guān)鍵字),new開辟空間在堆區(qū)或者自由存儲區(qū)(C plus plus原話:if the object is created by using new,it resides in heap memory, or the free store and its destructor is called automatically when you use delete to free the memory),malloc開辟在堆區(qū);
- C++有引用,C中沒有;
- const不同,C中的const叫只讀變量,只是無法做左值的變量;C++中的const是真正的常量,但也有可能退化成c語言的常量,默認生成local符號;
- C++靈活、可擴展、易于維護,耦合性較低,C語言耦合性較高。
8. malloc和new區(qū)別
-
malloc和new都是在堆上開辟內(nèi)存的。malloc只負責開辟內(nèi)存,沒有初始化功能,需要用戶自己初始化;new不但開辟內(nèi)存,還可以進行初始化,如new int(10);表示在堆上開辟了一個4字節(jié)的int整形內(nèi)存,初始值是10,再如new int[10] ();表示在堆上開辟了一個包含10個整形元素的數(shù)組,初始值都為0。
-
malloc是函數(shù),開辟內(nèi)存需要傳入開辟空間的大小(字節(jié)數(shù)),返回類型為void*,表示分配的堆內(nèi)存的起始地址,因此malloc的返回值需要轉(zhuǎn)換成指定類型的地址;new是運算符,開辟內(nèi)存需要指定類型,返回指定類型的地址,因此不需要類型轉(zhuǎn)換。
int *p1 = (int*)malloc(sizeof(int)); //根據(jù)傳入字節(jié)數(shù)開辟內(nèi)存,沒有初始化 int *p2 = new int(0); //根據(jù)指定類型int開辟一個整形內(nèi)存,初始化為0int *p3 = (int*)malloc(sizeof(int)*100); //開辟400個字節(jié)的內(nèi)存,相當于包含100個整形元素的數(shù)組,沒有初始化 int *p4 = new int[100](); //開辟400個字節(jié)的內(nèi)存,100個元素的整形數(shù)組,元素都初始化為0 -
malloc開辟內(nèi)存失敗返回nullptr(空指針),new開辟內(nèi)存失敗拋出bad_alloc類型的異常,需要捕獲異常才能判斷內(nèi)存開辟成功或失敗,new運算符其實是operator new函數(shù)的調(diào)用,它底層實現(xiàn)也是調(diào)用的malloc函數(shù),new它比malloc多的就是初始化功能,對于類類型來說,所謂初始化,就是調(diào)用相應的構(gòu)造函數(shù)。
-
malloc開辟的內(nèi)存是通過free來釋放的;而new單個元素內(nèi)存,用的是delete,如果new[]數(shù)組,用的是**delete[]**來釋放內(nèi)存的。
-
malloc開辟內(nèi)存只有一種方式,而new有四種分別是普通的new(內(nèi)存開辟失敗拋出bad_alloc異常), nothrow版本的new,const new以及定位new。
-
new/delete會調(diào)用對象的構(gòu)造函數(shù)/析構(gòu)函數(shù)以完成對象的構(gòu)造/析構(gòu),而malloc不會
-
new/delete可以被重載,但malloc/free不能
-
new/delete 和 new[]/delete[] 在自定義的類類型對象有析構(gòu)函數(shù)時,不能混用
9. 指針和引用區(qū)別
- 指針本質(zhì)是地址,有自己的一塊空間,引用本質(zhì)是某一個變量的別名;
- 指針初始化可以為空,引用初始化必須有確定的對象(取別名的本體要存在,否則不存在別名一說);
- 指針大小為指針本身的大小,為4字節(jié)(32位機器為4,64位機器為8),引用的大小是變量的大小;
- 指針可以改變指向,但引用不行(一旦為A的別名,不能再成為B的別名);
- 引用比指針安全(指針可能存在野指針的情況);
10.C++中內(nèi)存分區(qū)
-
五大分區(qū):棧、自由存儲區(qū)、堆、全局/靜態(tài)存儲區(qū)、常量存儲區(qū)
-
棧區(qū):編譯器自行開辟自行釋放的內(nèi)存區(qū),一半存放局部變量和函數(shù)參數(shù)等。
-
自由存儲區(qū):一般是malloc開辟的空間,與堆區(qū)類似,但不等價于堆區(qū),幾乎所有的編譯器都用堆來實現(xiàn)自由存儲(堆是操作系統(tǒng)維護的一塊內(nèi)存,而自由存儲是C++通過new/delete實現(xiàn)動態(tài)內(nèi)存分配和釋放對象的抽象概念)。
-
堆區(qū):由程序員手動開辟手動釋放,一般是new開辟的空間(new開辟的也可能在自由存儲區(qū)),如果程序員不釋放,OS可能回收,但也可能造成內(nèi)存泄露。
-
全局/靜態(tài)存儲區(qū):存放全局變量,靜態(tài)變量(static關(guān)鍵字修飾的),一般是被共享的。
-
常量存儲區(qū):保存常量,這部分的數(shù)據(jù)不允許被修改。
11. 野指針
-
野指針是指向不可用內(nèi)存的指針(并不是指向nullptr,不能用if判斷一個指針是否是野指針,良好的編程習慣是杜絕野指針的唯一方法)。
-
野指針形成原因:
-
指針變量沒有被初始化。任何指針變量剛被創(chuàng)建時不會自動生成nullptr指針,它的指向是隨機的,在創(chuàng)建指針時應當被初始化,要么將指針指向nullptr,要么讓它指向合法的地址。
-
指針被free和delete(只是將內(nèi)存釋放,并沒有把指針干掉)后沒有置為nullptr。
-
指針操作超出了變量的作用范圍,比如某個函數(shù)結(jié)束后,其局部變量會銷毀,此時如果指針指向該變量,在其他地方是不能使用的。
int *p; {int a;p=&a;cout<<*p<<endl; } cout<<*p<<endl; //此時p成為野指針,因為其所指向的局部變量a過了生命期
-
12. 類的默認函數(shù)
一個空類會生成以下默認函數(shù):默認構(gòu)造函數(shù)、默認析構(gòu)函數(shù)、默認拷貝構(gòu)造函數(shù)、默認拷貝賦值函數(shù)
13. class和struct區(qū)別
-
class默認繼承訪問權(quán)限為private繼承,而struct是public繼承
-
class默認訪問屬性是private,而struct是public
-
class具有繼承、多態(tài)等性質(zhì),而struct不具備
-
class能夠定義模板函數(shù),而struct不行
14. 構(gòu)造函數(shù)中初始值列表的必要性
在執(zhí)行構(gòu)造函數(shù)時,當執(zhí)行到函數(shù)體內(nèi)部時,所有的數(shù)據(jù)成員就已經(jīng)在內(nèi)存中創(chuàng)建,并利用類內(nèi)默認值初始化(如果類內(nèi)提供了初始值)。
對于引用類型的成員或是有const修飾符的成員,必須要利用初始值列表進行初始化,否則將會錯過初始化的時機,無法完成初始化。
- 必須在初始值列表中定義的數(shù)據(jù)成員
- 引用類型的成員
- 有const修飾符的成員
- 沒有默認構(gòu)造函數(shù)的類類型
15. 虛函數(shù)
-
虛函數(shù)是C++實現(xiàn)多態(tài)的重要保障,當類中有虛函數(shù)時,就存在一個虛函數(shù)指針指向虛函數(shù)表,虛函數(shù)表存放著虛函數(shù)的地址
-
派生類繼承基類時,虛函數(shù)指針和虛函數(shù)表都繼承了,當派生類重寫基類的虛函數(shù)后,派生類的虛函數(shù)表存的地址就變成了派生類重寫的虛函數(shù)地址
-
當基類指針或引用指向派生類時,就產(chǎn)生了多態(tài)
-
含有純虛函數(shù),則為抽象類,不能被實例化
virtual void get() = 0; //純虛函數(shù)
16. 繼承中的訪問權(quán)限
-
派生類繼承基類
class Student :public Person{ //若省略public,則默認為private繼承... } -
若派生類public繼承基類,派生類能否訪問基類中的成員,取決于其在基類中的訪問限定聲明;若為private,則派生類不能訪問;一般我們采用protected來修飾基類中的成員,以實現(xiàn)在派生類中的訪問權(quán)限
class Base { private:int m_pri; //private 成員, 在基類以外的地方,均不能被訪問 protected:int m_pro; //protected 成員, 在派生類中可被訪問,在其他地方不能被訪問 public:int m_pub; //public 成員, 在類內(nèi)類外均可被訪問 }; class PubDerv :public Base {void foo() {m_pri = 10;//錯誤:不能訪問 Base 類私有成員m_pro = 1; //正確:可以訪問 Base 類受保護成員} }; void test() {Base b;b.m_pro = 10;//錯誤:不能訪問 Base 受保護成員 } -
protected修飾符可以看作是private和public的混合物
- 和公有成員類似,基類中受保護(protected)的成員在其派生類里是可以訪問的
- 和私有成員類似,基類中受保護的成員在類外是無法訪問的
17. C++防止頭文件重復包含
兩種方式防止頭文件重復包含
-
ifndef語句,如下為myHeader.h
#ifndef MYHEADER_H #define MYHEADER_H // 頭文件主題 #endif // !MYHEADER_H -
#pragma once語句
#pragma once // 頭文件主題
由于第二種是windows下的,建議使用第一種
18.TCP連接和斷開
-
三次握手
-
四次揮手
19. 長連接和短連接
長連接和短連接指的是TCP的長連接和TCP的短連接。
- TCP長連接:TCP長連接指建立連接后保持連續(xù)而不斷,如一段時間沒有數(shù)據(jù)交互,保活定時器超時后,服務端會發(fā)送探測報文給客戶端檢測客戶端是否在線。連接—傳輸數(shù)據(jù)—保活—傳輸數(shù)據(jù)—保活—…。
- TCP短連接:指建立并傳輸數(shù)據(jù)完成后立即斷開。連接—傳輸數(shù)據(jù)—斷開。
- 應用場景:長連接適用單對單通信且連接數(shù)不太多的情況,短連接適用于連接數(shù)多且經(jīng)常需要更換連接對象的情況。
- 說明:HTTP/1.0中,默認使用短連接,HTTP/1.1起,默認使用長連接。HTTP的長連接也不是永久保持連接,保活計時器超時后,探測報文沒回應,那么也會斷開連接。
20. 進程和線程區(qū)別
- 進程是操作系統(tǒng)進行資源分配和調(diào)度的基本單位,而線程是操作系統(tǒng)進行運行調(diào)度的最小單位,包含在進程中,是進程中實際工作的單位;
- 線程存在于進程之中,與進程是多對一的關(guān)系,一個進程可以存在多個線程,這些線程共享進程資源;
- 進程有自己的獨立地址空間,每啟動一個進程,系統(tǒng)就會為他分配地址空間,建立數(shù)據(jù)表來維護代碼段、堆棧段和數(shù)據(jù)段,這種操作非常昂貴;線程則共享進程的空間及數(shù)據(jù)。
- 線程之間的通信更方便(共享全局變量、靜態(tài)變量等數(shù)據(jù)),而進程通信需要以通信方式(管道、消息隊列、信號量、共享存儲、socket、streams)進行。
- 多進程程序更健壯,多線程程序只有有一個線程死掉,整個進程也死掉了。而一個進程死掉,不影響另外一個進程,因為進程有獨立空間。
21. 進程通信和線程通信
- 進程間通信方式(InterProcess Communication, IPC)
- (無名)管道(pipe):是一種半雙工的通信方式,數(shù)據(jù)只能單向流動,而且只能在具有親緣關(guān)系的進程間使用。進程的親緣關(guān)系通常是指父子進程關(guān)系。
- 命名管道(namedpipe):可以在無關(guān)進程之間交換數(shù)據(jù)。
- 消息隊列(messagequeue):是消息的鏈接表,存放在內(nèi)核中,一個消息隊列由一個標識符(隊列ID)來標識。消息隊列是面向記錄的,其中的消息具有特定的格式以及特定的優(yōu)先級;獨立于發(fā)送與接收進程,進程終止時,消息隊列及其內(nèi)容不會被刪除;可以實現(xiàn)消息的隨機查詢。
- 信號量(semophore):它是一個計數(shù)器,用于實現(xiàn)進程間的互斥與同步,而不是用于存儲進程間通信數(shù)據(jù)。用于進程間同步,若要在進程間傳遞數(shù)據(jù)需要結(jié)合共享內(nèi)存。
- 共享內(nèi)存(shared memory):指兩個或多個進程共享一個給定的存儲區(qū)。共享內(nèi)存是最快的IPC,因為進程是直接對內(nèi)存進行存取;因為多個進程可以同時操作,所以需要同步;信號量+共享內(nèi)存通常結(jié)合在一起使用,信號量用來同步對共享內(nèi)存的訪問。
- 套接字(socket):是一種進程間通信機制,與其他通信機制不同的是,它可用于不同設備及其間的進程通信。
- 總結(jié)
- 管道:速度慢,容量有限,只有父子進程能通訊
- 命名管道:任何進程間都能通訊,但速度慢
- 消息隊列:容量受到系統(tǒng)限制,且要注意第一次讀的時候,要考慮上一次沒有讀完數(shù)據(jù)的問題
- 信號量:不能傳遞復雜消息,只能用來同步
- 共享內(nèi)存:能夠很容易控制容量,速度快,但要保持同步,比如一個進程在寫的時候,另一個進程要注意讀寫的問題,相當于線程中的線程安全
- 線程間通信方式
- 鎖機制:互斥鎖、條件變量、讀寫鎖
- 信號量機制:無名線程信號量和命名線程信號量
22. 線程池
由于線程的創(chuàng)建和銷毀需要消耗資源,當線程頻繁創(chuàng)建和銷毀時或者線程創(chuàng)建和銷毀的時間大于線程執(zhí)行的時間,那么單純的采用線程創(chuàng)建/銷毀策略明顯不合適,所以有了線程池的概念,線程池中存放一定數(shù)量創(chuàng)建好的線程,需要用的時候拿出來用,用完之后再放到池子里。
-
線程池的好處就是
- 降低了頻繁創(chuàng)建銷毀線程的開銷;
- 提高了響應速度;
- 提高了線程的可管理性;
-
線程池組成部分
- 線程管理池:用于創(chuàng)建和管理線程池,有創(chuàng)建、銷毀、添加新任務
- 工作線程:線程池中的線程,其在沒有任務的時候處于等待狀態(tài),可以循環(huán)的執(zhí)行任務
- 任務隊列:用于存放沒有處理的任務,提供一種緩存機制
- 任務接口:每個任務必須實現(xiàn)接口,以供工作線程調(diào)度任務的執(zhí)行,規(guī)定了任務的入口及執(zhí)行結(jié)束的收尾工作和任務的執(zhí)行狀態(tài)等
-
線程池工作流程
- 判斷順序:核心線程池、隊列、線程池
23. 實踐中優(yōu)化MySQL
- SQL語句及索引的優(yōu)化;
- 數(shù)據(jù)表結(jié)構(gòu)的優(yōu)化;
- 系統(tǒng)配置的優(yōu)化;
- 硬件的優(yōu)化。
上述四條從效果上來說,第一條影響最大,后面越來越小。
24. 設計高并發(fā)的系統(tǒng)
- 數(shù)據(jù)庫的優(yōu)化,包括合理的事務隔離級別、SQL語句優(yōu)化、索引優(yōu)化
- 使用緩存,盡量減少數(shù)據(jù)庫IO操作;
- 分布式數(shù)據(jù)庫、分布式緩存;
- 服務器的負載均衡。
25. 小端和大端機器
- 小端/大端的區(qū)別是指低位數(shù)據(jù)存儲在內(nèi)存低位還是內(nèi)存高位。
- 小端機器:數(shù)據(jù)低位存儲在內(nèi)存低位,數(shù)據(jù)高位則存儲在內(nèi)存高位;
- 大端機器:數(shù)據(jù)低位存儲在內(nèi)存高位,數(shù)據(jù)高位存儲在內(nèi)存低位。
- 目前絕大多數(shù)都是小端機器,符合人們的邏輯思維的數(shù)據(jù)存儲方式。
26. strlen和sizeof區(qū)別
- strlen就算字符串長度
- sizeof會將結(jié)束符算進去
總結(jié)
- 上一篇: 瑞星、360、金山卫士、BaiduPla
- 下一篇: 斗破苍穹[C++]