1.線程的優點:
? 創建?個新線程的代價要?創建?個新進程?得多
創建好線程只要調度就好了
? 與進程之間的切換相?,線程之間的切換需要操作系統做的?作要少很多
為什么?
? 最主要的區別是線程的切換虛擬內存空間依然是相同的,但是進程切換是不同的。這兩種上 下?切換的處理都是通過操作系統內核來完成的。
內核的這種切換過程伴隨的最顯著的性能 損耗是將寄存器中的內容切換出。
? 另外?個隱藏的損耗是上下?的切換會擾亂處理器的緩存機制。簡單的說,?旦去切換上下 ?,處理器中所有已經緩存的內存地址?瞬間都作廢了。還有?個顯著的區別是當你改變虛 擬內存空間的時候,處理的?表緩沖 TLB (快表)會被全部刷新,這將導致內存的訪問在? 段時間內相當的低效。但是在線程的切換中,不會出現這個問題,當然還有硬件cache。
? 線程占?的資源要?進程少
? 能充分利?多處理器的可并?數量
線程是調度的基本單位
? 在等待慢速I/O操作結束的同時,程序可執?其他的計算任務
多進程也可以做,也是進程的優點
? 計算密集型應?,為了能在多處理器系統上運?,將計算分解到多個線程中實現
加密,解密,壓縮。
通俗解釋:計算機多個CPU,把計算任務拆分成多份,分別跑
最好只創建跟CPU對等的線程
? I/O密集型應?,為了提?性能,將I/O操作重疊。線程可以同時等待不同的I/O操作。
線程可以多創建。概率上總會有線程讀數據,執行。
線程切換?
(1)進程切換:PCB切成另一個進程的PCB。
task_struct*current:表示OS全局指針,current指向當前進程,寄存器。換跟換。
(2)線程切換:?
成本比進程低為什么?
同一個進程的線程切換:
不用保存CR3寄存器。------>看不出來誰的成本更低
頁表不用切換。
?
為什么進程切換成本高???
進程切換的兩個大緩存:
(1)對用戶數據進行cache:
cache緩存:
Cache(緩存)是計算機系統中的一種高速存儲器,位于CPU和主內存之間,用于暫時存儲CPU可能頻繁訪問的數據和指令。Cache的主要作用是減少CPU訪問主內存的次數,從而提高系統的整體性能。
Cache的訪問速度非常快,通常在幾個時鐘周期內就可以完成數據的讀取或寫入。通過將CPU頻繁訪問的數據和指令存儲在Cache中,CPU可以直接從Cache中讀取數據,從而大大減少了訪問主內存的次數,提高了系統性能(2)TLB快表
總結:
進化切換,會導致TLB和Cache失效,下次運行,需要重新緩存
線程不切換頁表,不會出現緩存失效,所以線程切換成本更低。
2.線程的缺點?
? 性能損失
? ?個很少被外部事件阻塞的計算密集型線程往往?法與其它線程共享同?個處理器。如果計 算密集型線程的數量?可?的處理器多,那么可能會有較?的性能損失,這?的性能損失指 的是增加了額外的同步和調度開銷,?可?的資源不變。
? 健壯性降低
? 編寫多線程需要更全?更深?的考慮,在?個多線程程序?,因時間分配上的細微偏差或者 因共享了不該共享的變量?造成不良影響的可能性是很?的,換句話說線程之間是缺乏保護 的。
? 缺乏訪問控制
? 進程是訪問控制的基本粒度,在?個線程中調?某些OS函數會對整個進程造成影響。
缺乏訪問控制,也是優點,可以自由地訪問資源
? 編程難度提?
? 編寫與調試?個多線程程序?單線程程序困難得多
3.線程異常
? 單個線程如果出現除零,野指針問題導致線程崩潰,進程也會隨著崩潰
? 線程是進程的執?分?,線程出異常,就類似進程出異常,進?觸發信號機制,終?進程,進程 終?,該進程內的所有線程也就隨即退出
Linux進程VS線程
進程和線程
? 進程是資源分配的基本單位
? 線程是調度的基本單位
? 線程共享進程數據,但也擁有??的?部分“私有”數據::
? 線程ID
? ?組寄存器,線程的上下文
“線程要有自己的獨立上下文數據 - >線程是可以被獨立調度的。”
? 棧
線程都有自己獨立的棧結構->線程是一個動態的概念(“有生命周期”)。
? errno “錯誤碼”
? 信號屏蔽字
? 調度優先級
進程大部分資源獨占,少量資源共享
線程相反。
進程被多個線程共享
同?地址空間,因此Text Segment、Data Segment都是共享的,如果定義?個函數,在各線程中都可以調 ?,如果定義?個全局變量,在各線程中都可以訪問到,除此之外,各線程還共享以下進程資源和環境:
? ?件描述符表
? 每種信號的處理?式(SIG_IGN、SIG_DFL或者?定義的信號處理函數)
? 當前?作?錄
? ??id和組id
進程和線程的關系如下圖:
關于進程線程的問題
? 如何看待之前學習的單進程?具有?個線程執?流的進程
?Linux線程控制
????????創建一個線程
功能:創建?個新的線程 原型:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void * (*start_routine)(void*), void *arg);
(1)參數: thread:返回線程ID attr:設置線程的屬性,attr為NULL表?使?默認屬性
(2)start_routine:是個函數地址,線程啟動后要執?的函數
(3)arg:傳給線程啟動函數的參數
(4)返回值:成功返回0;失敗返回錯誤碼
代碼?
Makefile
test_thread:TestThread.ccg++ -o $@ $^
.PHONY:clean
clean:rm -f test_thread
?TestThread.cc:自己寫的√
#include<iostream>
#include<pthread.h>
#include<string>
#include<unistd.h>using namespace std;void* threadstat(void*args){string name = (const char*)args;while(true){cout<<"我是子進程,name:"<<name<<endl;sleep(1);}return nullptr;
}int main()
{pthread_t tid;pthread_create(&tid,nullptr,threadstat,(void*)"thread-1");while(true){cout<<"我是主線程"<<endl;sleep(10);}
}
?編譯不通過
不是系統調用
test_thread:TestThread.ccg++ -o $@ $^ -lpthread .PHONY:clean clean:rm -f test_thread
需要加上庫pthread,-l:引入庫文件的操作
編譯通過:結果,沒有子進程,兩個死循環跑起來
?只有一個進程,殺掉,兩個都沒了。
理解?
ps -aL:查看線程
創建線程成功:
新線程執行:threadrun,新線程入口
編譯出來就是一組虛擬地址main函數,就是另一組虛擬地址,表示代碼和數據。
pid:是一樣的,屬于同一個進程
TTY:終端一樣
LWP-light weight process:輕量級進程
“主進程,pid和lwp一樣”
“線程pid和主進程一樣,lwp與pid不一樣”task_strut里面有兩個數據
pid_t pid;
pid_t lwp;
問題?
1.關于調度的時間片問題:時間片是等分給不同的線程。
2.線程異常??
代碼:√
#include<iostream>
#include<pthread.h>
#include<string>
#include<unistd.h>using namespace std;void* threadstat(void*args){string name = (const char*)args;while(true){cout<<"我是子進程,name:"<<name<<endl;sleep(1);int a = 1;a/=0;}return nullptr;
}int main()
{pthread_t tid;pthread_create(&tid,nullptr,threadstat,(void*)"thread-1");while(true){cout<<"我是主線程"<<endl;sleep(10);}
}
任何一個線程崩潰,都會導致整個進程崩潰
3.消息混在一起?
顯示器是共享資源
引入pthread庫?
為什么會有一個庫?這個庫是什么東西?
c++11多線程demo
c++11也引入了多線程叫做
thread
?
#include <iostream>
#include <string>
#include <thread>void hello(){while (true){std::cout << "新線程: hello world, pid: " << getpid() << std::endl;sleep(1);}}int main(){std::thread t(hello);while (true){std::cout << "我是主線程..." << ", pid: " << getpid() << std::endl;sleep(1);}t.join();return 0;}
報錯,由于沒有使用原生線程庫pthread.?
test_thread:TestThread.ccg++ -o $@ $^ -std=c++11 - .PHONY:clean clean:rm -f test_thread
這樣就OK
test_thread:TestThread.ccg++ -o $@ $^ -std=c++11 -lpthread .PHONY:clean clean:rm -f test_thread
?c++11的多線程,在linux下,本質就是堆pthread的封裝
?
c++,在不同的環境下,對線程進程不同的封裝。?
所有語言的線程都要獨立進行封裝
?線程控制的接口
創建線程:pthread_create
功能:創建?個新的線程 原型:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void * (*start_routine)(void*), void *arg);
(1)參數: thread:返回線程ID attr:設置線程的屬性,attr為NULL表?使?默認屬性
(2)start_routine:是個函數地址,線程啟動后要執?的函數
(3)arg:傳給線程啟動函數的參數
(4)返回值:成功返回0;失敗返回錯誤碼
?終止線程:pthread_exit | pthred_cancle
如果需要只終?某個線程?不終?整個進程,可以有三種?法:
pthread_exit函數:
功能:線程終?
原型:void pthread_exit(void *value_ptr);參數:value_ptr:value_ptr不要指向?個局部變量。
返回值:?返回值,跟進程?樣,線程結束的時候?法返回到它的調?者(??)
需要注意,pthread_exit或者return返回的指針所指向的內存單元必須是全局的或者是?malloc分配的, 不能在線程函數的棧上分配,因為當其它線程得到這個返回指針時線程函數已經退出了。 ?
pthread_cancel函數
功能:取消?個執?中的線程 原型: int pthread_cancel(pthread_t thread);參數: thread:線程ID返回值:成功返回0;失敗返回錯誤碼
線程等待:pthread_join
為什么需要線程等待?
? 已經退出的線程,其空間沒有被釋放,仍然在進程的地址空間內。
? 創建新的線程不會復?剛才退出線程的地址空間。
?線程創建好之后,新線程要被主線程等待
---->不然會觸發,類似僵尸進程,內存泄漏
功能:等待線程結束
原型int pthread_join(pthread_t thread, void **value_ptr);參數:thread:線程IDvalue_ptr:它指向?個指針,后者指向線程的返回值返回值:成功返回0;失敗返回錯誤碼
調?該函數的線程將掛起等待,直到id為thread的線程終?。thread線程以不同的?法終?,通過 pthread_join得到的終?狀態是不同的,總結如下:
1. 如果thread線程通過return返回,value_ptr所指向的單元?存放的是thread線程函數的返回值。
2. 如果thread線程被別的線程調?pthread_cancel異常終掉,value_ptr所指向的單元?存放的是常 數PTHREAD_CANCELED。
3. 如果thread線程是??調?pthread_exit終?的,value_ptr所指向的單元存放的是傳給 pthread_exit的參數。
4. 如果對thread線程的終?狀態不感興趣,可以傳NULL給value_ptr參數。
#include<iostream>
#include<pthread.h>
#include<string>
#include<unistd.h>
#include<cstdio>using namespace std;int flag = 100;
void showid(pthread_t &tid)//減少拷貝
{printf("tid: 0x%lx",tid);
}string Formatid(const pthread_t &tid){char id[64];snprintf(id,sizeof(id),"0x%lx",tid);return id;
}void* threadstat(void*args){string name = (const char*)args;pthread_t id = pthread_self();int cnt = 5;while(cnt){cout<<"我是子進程,name:"<<name<<"我的Id是"<<Formatid(id)<<endl;;sleep(1);cnt--;flag++;}return nullptr;
}int main()
{pthread_t tid;pthread_create(&tid,nullptr,threadstat,(void*)"thread-1");showid(tid);int cnt = 6;while(cnt--){cout<<"我是主線程"<<"我的Id是"<<Formatid(pthread_self())<<"flag:"<<flag<<endl;sleep(1);}void *ret = nullptr; // ret也是一個變量!!也有空間哦!// 等待的目標線程,如果異常了,整個進程都退出了,包括main線程,所以,join異常,沒有意義,看也看不到!// jion都是基于:線程健康跑完的情況,不需要處理異常信號,異常信號,是進程要處理的話題!!!pthread_join(tid, &ret); // 為什么在join的時候,沒有見到異常相關的字段呢??std::cout << "ret is : " << (long long int)ret << std::endl;return 0;
}
返回值存放在ret里面,可以查看,長整型。
返回線程的tid:pthread_self
誰調用,就獲取誰的tid
std::string FormatId(const pthread_t &tid)
{char id[64];snprintf(id, sizeof(id), "0x%lx", tid);return id;
}
?線程傳參和返回值
主線程在調用FormatId,
新線程也會調用。
該函數被稱為可重入函數?
?代碼1:驗證join可以去的線程執行完后的退出碼/返回值
#include<iostream> #include<unistd.h> #include<pthread.h> #include<string> using namespace std;void* routine(void* arg){string name = static_cast<const char*>(arg);int cnt = 5;while(cnt--){cout<<"我是子線程,名字:"<<name<<endl;sleep(1);}return (void*)10; }int main(){pthread_t tid;pthread_create(&tid,nullptr,routine,(void*)"thread-1");int cnt = 5;while(cnt--){cout<<"我是主線程"<<endl;sleep(1);}void* ret = nullptr;pthread_join(tid,&ret);cout<<"子進程退出,退出碼為:"<<(long long)ret<<endl; }
1.main函數結束,代表主線程結束,也代表進程結束
2.新線程對應的入口函數,運行結束,代表當前線程運行結束。
3.問題:給線程傳遞的參數和返回值,可以是任意類型?代碼2:30min證明::給線程傳遞的參數和返回值,可以是任意類型 ,下面的例子是類類型
#include<iostream> #include<unistd.h> #include<pthread.h> #include<string> using namespace std;class Task{public:Task(int a,int b):_a(a),_b(b){}int Cal(){return _a+_b;}~Task() {}private:int _a;int _b; }; class Result{public:Result(int result):_result(result){}int getresult(){return _result;}~Result(){}private:int _result; }; void* routine(void* arg){Task*t = static_cast<Task*>(arg);sleep(1);Result* r = new Result(t->Cal());sleep(1);return r; }int main(){pthread_t tid;Task* t = new Task(20,10);pthread_create(&tid,nullptr,routine,t);Result* ret = nullptr;pthread_join(tid,(void**)&ret);int n = ret->getresult();cout<<"子線程的返回值是:"<<n<<endl; }