多執行緒程式設計(二)——執行緒結束後的處理&主服務存活方法
應用場景:
伺服器,建立了多個服務子執行緒,而後主執行緒“無所事事”,程序會被關閉,導致子執行緒sleepA和sleepB無法正常執行完成。(假設sleepA()和sleepB()都是沉睡若干秒的函式,這個肯定要比程式執行到main結束那幾步所花費時間要長)
int main() { int iRet = 0; iRet = pthread_create(&thread_a_id,NULL,sleepA,NULL); if(iRet != 0) { printf("create failed!\n"); } iRet = pthread_create(&thread_b_id,NULL,sleepB,NULL); if(iRet != 0) { printf("create failed!\n"); } }
想讓sleepA和sleepB能夠正常持續執行下去,程序必須賴下去——讓主執行緒不停止。低劣的做法如下:
int main() { int iRet = 0; iRet = pthread_create(&thread_a_id,NULL,sleepA,NULL); if(iRet != 0) { printf("create failed!\n"); } iRet = pthread_create(&thread_b_id,NULL,sleepB,NULL); if(iRet != 0) { printf("create failed!\n"); } while(1) { sleep(60); } }
提倡的做法如下:
這個方法用了pthread_join()等待指定的服務執行緒結束並釋放資源,注意,是阻塞的等待!因為不會向下執行,所以主服務得以存活。因為是阻塞的,在join a的時候就絕對不會join b了,所以如上程式碼如果b先結束,程式也不會前進,等待a結束才會向下執行。int main() { int iRet = 0; iRet = pthread_create(&thread_a_id,NULL,sleepA,NULL); if(iRet != 0) { printf("create failed!\n"); } iRet = pthread_create(&thread_b_id,NULL,sleepB,NULL); if(iRet != 0) { printf("create failed!\n"); } pthread_join(thread_a_id,NULL); pthread_join(thread_b_id,NULL); }
這裡注意,預設情況新建執行緒是joinable的,可以用join,如果設定成別的,也會join不成。
============================================================================
另外,這個join之後不一定就主程序結束,這樣用就太單純了。
應用一:
比如本來要持續執行服務的子執行緒,可以pthread_join()之後再pthread_create(),這樣就能起到內部容錯和保活的作用。
int main()
{
int iRet = 0;
iRet = pthread_create(&thread_a_id,NULL,sleepA,NULL);
if(iRet != 0)
{
printf("create failed!\n");
}
printf("before join a\n");
pthread_join(thread_a_id,NULL);
printf("after join a\n");
iRet = pthread_create(&thread_a1_id,NULL,sleepA,NULL);
if(iRet != 0)
{
printf("create failed!\n");
}
}
但這只是想象,實際上並不能成功,問題在哪呢?
# ./a.out
before join a
sleepA called!
now sleepA over!
after join a
可以看到,程式不可能在join之後直接終結,後續流程還是要走,但是卻並沒有第二次執行sleepA(),如果只是第二次pthread_create失敗,也並沒有列印失敗。
關於id值,使用和之前相同的thread_a_id或者單獨的一個thread_a1_id結果都是一樣的不成功!
關於函式,使用sleepA或者sleepB都一樣的不成功,所以也和函式沒關係!
所以,pthread_join()之後到底算個什麼狀態?為什麼ptread_create不行了?
其實,上邊是我故意做的錯誤示範,第二次pthread_create()之後主執行緒結束了啊,缺少第二個join來阻塞住程序啊!!!!!!後根一個該id對應的join操作,解決!
# ./a.out
before join a
sleepA called!
now sleepA over!
after join a
now sleepB over!
============================================================
應用二:
但是如果再次create的程式碼在後邊新增,不能實現持續的容錯重啟,所以可以改成一個大迴圈,內部先pthread_create(),再pthread_join()阻塞,迴圈結束,再pthread_create(),再pthread_join()。。。。。。如此往復。int main()
{
int iRet = 0;
while(1)
{
iRet = pthread_create(&thread_a_id,NULL,sleepA,NULL);
if(iRet != 0)
{
printf("create failed!\n");
}
printf("before join a\n");
pthread_join(thread_a_id,NULL);
printf("after join a\n");
}
}
執行結果如下,不強制關閉不會停。
# ./a.out
before join a
sleepA called!
now sleepA over!
after join a
before join a
sleepA called!
now sleepA over!
after join a
before join a
sleepA called!
now sleepA over!
after join a
before join a
sleepA called!
now sleepA over!
after join a
before join a
sleepA called!
^C
應用三:
這個執行緒的處理還可以這樣用,在一個大迴圈內,等待訊號,有一個請求到來就新建一個執行緒單獨去處理這個請求,然後結束再回來。(這裡假設a是訊號量,是一個全域性變數,有一個單獨的執行緒傳訊號——改a的值,通過訊號量a來觸發服務sleepA)
#include <stdio.h>
#include <pthread.h>
static pthread_t thread_a_id;
static pthread_t thread_a1_id;
static pthread_t thread_b_id;
static int i = 0;
static int a = 0;
void* sleepA(void *p)
{
printf("sleepA called!\n");
sleep(1);
printf("now sleepA over!\n");
return NULL;
}
void* setA(void *p)
{
while(1)
{
sleep(2);
a = 1;
}
return NULL;
}
int main()
{
int iRet = 0;
iRet = pthread_create(&thread_b_id,NULL,setA,NULL);
if(iRet != 0)
{
printf("create failed!\n");
}
while(1)
{
if(a == 1)
{
iRet = pthread_create(&thread_a_id,NULL,sleepA,NULL);
if(iRet != 0)
{
printf("create failed!\n");
}
printf("before join a\n");
pthread_join(thread_a_id,NULL);
printf("after join a\n");
a = 0;
}
}
}
如上,兩秒一置1,sleepA只睡1秒,還忙得過來,假如1秒一置1,sleepA睡2秒呢?睡5秒呢?程式碼變動如下
void* sleepA(void *p)
{
printf("sleepA called!\n");
sleep(5);
printf("now sleepA over!\n");
return NULL;
}
void* setA(void *p)
{
while(1)
{
sleep(1);
a = 1;
}
return NULL;
}
明顯會慢得多!!!因為pthread_join()是阻塞的,這樣一次只可能有一個執行緒去處理請求,不能達到想要的效果。
應用四:
這時候就不能用pthread_join()了,要用pthread_detach(),這個非阻塞,等於生完孩子就不管了,放養,最後也能回收執行緒資源(這些join和detach什麼的,主要就是等著回收資源)。
#include <stdio.h>
#include <pthread.h>
static pthread_t thread_a_id;
static pthread_t thread_a1_id;
static pthread_t thread_b_id;
static int i = 0;
static int a = 0;
void* sleepA(void *p)
{
printf("sleepA called!\n");
sleep(5);
printf("now sleepA over!\n");
return NULL;
}
void* setA(void *p)
{
while(1)
{
sleep(1);
a = 1;
}
return NULL;
}
int main()
{
int iRet = 0;
iRet = pthread_create(&thread_b_id,NULL,setA,NULL);
if(iRet != 0)
{
printf("create failed!\n");
}
while(1)
{
if(a == 1)
{
iRet = pthread_create(&thread_a_id,NULL,sleepA,NULL);
if(iRet != 0)
{
printf("create failed!\n");
}
printf("before detach a\n");
pthread_detach(thread_a_id);
printf("after detach a\n");
a = 0;
}
}
}
pthread_detach(),這個在子執行緒(pthread_self())中和父執行緒(child_thread_id)中用法略有不同,但本質一樣,引數都是要detach的那個執行緒的id。
相關推薦
多執行緒程式設計(二)——執行緒結束後的處理&主服務存活方法
應用場景: 伺服器,建立了多個服務子執行緒,而後主執行緒“無所事事”,程序會被關閉,導致子執行緒sleepA和sleepB無法正常執行完成。(假設sleepA()和sleepB()都是沉睡若干秒的函式,這個肯定要比程式執行到main結束那幾步所花費時間要長) int ma
Linux學習之多執行緒程式設計(二)
言之者無罪,聞之者足以戒。 ——《詩序》 (二)、執行緒的基本控制 1、終止程序: 如果程序中的任意一個程序呼叫了exit、_exit、_Exit,那麼整個程序就會終止 普通的單個程序有以下3種退出方式,這樣不會終止程序: (1)從啟動例程中返回,返回值是執行緒的退
多執行緒程式設計(二)——面試題,每個執行緒只打印一種字元,多個執行緒協同順序列印n次字串(求大神的其他實現方案)
(這個是歡聚時刻(YY)的筆試題,在筆試的時候沒有寫出來,後來自己寫了出來,希望大神能給出更優秀的解決方案……ps:現在面試官總有面試時問筆試程式設計題思路的習慣,呵呵) 題目簡述: 輸入一個字串以
C++——多執行緒程式設計(二)std::mutex 執行緒同步、解決資源競爭問題
前言 執行緒同步 這裡的“同”不是同時、一起執行的意思,而是指協同、協助、互相配合。執行緒同步是指多個執行緒協同步調,按預定的先後次序進行執行。 執行緒A和B一塊配合,A執行到一定程度時要依靠B的某個結果,於是停下來,示意B執行;B依言執行,再將結果給A;
java多執行緒程式設計(二)
java多執行緒程式設計(二) volatile、synchronized及鎖對比 volatile的使用優化 1、追加位元組能優化效能 如果佇列的頭節點和尾節點都不足64位元組的話,處理器會將他們都讀到同一個快取記憶體行中,在多處理器 下每個處理器都會
c++11多執行緒程式設計(二):joining和detaching 執行緒
Joining執行緒 執行緒一旦啟動,另一個執行緒可以通過呼叫std::thread物件上呼叫join()函式等待這個執行緒執行完畢std::thread th(funcPtr); th.join(); 看一個例子主執行緒啟動10個工作執行緒,啟動完畢後,main函式等待
Linux 多執行緒程式設計(二)
執行緒管理執行緒管理包含了執行緒的建立、終止、等待、分離、設定屬性等操作。1 執行緒 ID執行緒 ID 可以看作為執行緒的控制代碼,用來引用一個執行緒。Pthreads 執行緒有一個 pthread_t 型別的 ID 來引用。執行緒可以通過呼叫 pthread_self()函
python多執行緒程式設計(二)--threading模組
threading模組物件 物件 描述 Thread 一個執行緒的執行物件 Lock 鎖物件 RLock 可重入鎖物件,使單執行緒可以再次獲得已經獲得了的鎖(遞迴鎖定) Condition 條件變數,讓一個執行緒停下來,等待其它執行緒滿足了某個條件 Event
Java多執行緒程式設計-(1)-執行緒安全和鎖Synchronized概念
一、程序與執行緒的概念 (1)在傳統的作業系統中,程式並不能獨立執行,作為資源分配和獨立執行的基本單位都是程序。 在未配置 OS 的系統中,程式的執行方式
ava多執行緒程式設計-(4)-執行緒間通訊機制的介紹與使用
原文出自 : https://blog.csdn.net/xlgen157387/article/details/78195817 執行緒間通訊簡介 我們知道執行緒是作業系統中獨立的個體,但是這個單獨的個體之間沒有一種特殊的處理方式使之成為一個整體,執行緒之間沒有任何交
Java多執行緒程式設計-(3)-執行緒本地ThreadLocal的介紹與使用
原文出自 : https://blog.csdn.net/xlgen157387/article/details/78114278 ThreadLocal簡介 我們通過上兩篇的學習,我們已經知道了變數值的共享可以使用public static變數的形式,所有的執行緒都使
C++多執行緒系列(二)執行緒互斥
首先了解一下執行緒互斥的概念,執行緒互斥說白了就是在程序中多個執行緒的相互制約,如執行緒A未執行完畢,其他執行緒就需要等待! 執行緒之間的制約關係分為間接相互制約和直接相互制約。 所謂間接相互制約:一個系統中的多個執行緒必然要共享某種系統資源如共享CPU,共享印表機。間接制
Java 併發程式設計(二)執行緒狀態躍遷
執行緒的狀態: 1、新建狀態(New) 新建立了一個執行緒物件。 示例: Thread t = new Thread(); 2、就緒狀態(Runnable) 執行緒物件建立後,
多執行緒程式設計(2)—執行緒安全
1. 問題的引出 執行緒安全歸根結底可以說是記憶體安全,在jvm記憶體模型中,有一塊特殊的公共記憶體空間,稱為堆記憶體,程序內的所有執行緒都可以訪問並修改其中的資料,就會造成潛在的問題。因為堆記憶體空間在沒有保護機制的情況下,你放進去的資料,可能被別的執行緒篡改。如下程式碼: public cla
Java併發程式設計(二)多執行緒四種實現方式
Java實現多執行緒的方式 Java實現多執行緒的方式有4種: 繼承Thread方法、實現Runnable介面、實現Callable介面並通過FutureTask建立執行緒、使用ExecutorService。 其中,前兩種執行緒執行結果沒有返回值,後兩種是有返回值的。 1、繼承Th
java併發程式設計(二)多個執行緒多個鎖
多個執行緒多個鎖 多個執行緒多個鎖:多個執行緒,每個執行緒都可以拿到自己制定的鎖,分別獲得鎖之後,執行synchronized方法體的內容。就是在上次那個部落格上說道的鎖競爭的問題,是因為所有的執行緒過來以後都爭搶同一個鎖。如果說每個執行緒都可以或得到自己的鎖,這樣的話我們的鎖競爭問題就沒有了
Java併發程式設計(二)多執行緒程式設計
在上一節,我們介紹了程序與執行緒的概念,接下來介紹如何使用多執行緒(暫不介紹多程序)。 2. Thread物件 每個執行緒都對應一個Thread例項,存在兩種策略使用Thread類來建立併發程式。 直接進行執行緒的建立和管理,也就是當需要開啟一個非同
Python多執行緒與多程序程式設計(二) 就這麼簡單
""" <axiner>宣告:(錯了另刂扌丁我) (如若有誤,請記得指出喲,謝謝了!!!) """ 多程序程式設計>>>見上篇 什麼時候用多程序程式設計? 由於 GIL鎖,多執行緒無法充分多核優勢。即在耗cpu時,多執行緒無法去並行
java網路程式設計:9、基於TCP的socket程式設計(二)伺服器端迴圈監聽接收多個客戶端_多執行緒伺服器程式
宣告:本教程不收取任何費用,歡迎轉載,尊重作者勞動成果,不得用於商業用途,侵權必究!!! 文章目錄 一、核心程式碼編寫 1、伺服器端程式的編寫 2、客戶端程式的編寫 3、測試列印輸出 二、系列文章(java網路程式設計) 上篇講了基於tcp的程式設計的一些基礎知識
C#網路程式設計(二)------多執行緒socket例項
伺服器端程式碼: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net; using Sys