在一系列關聯的多任務的實時環境中,如果有一個任務發生失敗,可能導致所有任務產生連鎖反應,從而造成調度失控的局面。特別是對于核心控制設備尤其重要,為了解決這個問題,必須對每個任務進行實時監控。
?
在一系列關聯的多任務的實時環境中,如果有一個任務發生失敗,可能導致所有任務產生連鎖反應,從而造成調度失控的局面。特別是對于核心控制設備尤其重要,為了解決這個問題,必須對每個任務進行實時監控。
?
在JAVA環境中,一個任務一般是由一個獨立線程來引導實現的,獨立線程可能調用一系列子線程。如果在執行過程中,某一個線程發生異常(產生的原因很多,比如軟件升級、運行環境改變、系統資搶占等),那么該線程就會停止運行,直到下次任務重新被提交。對于實時環境來說當前任務是失敗的。我們無法預測和完全避免異常的發生,但是可以通過一些技術手段來跟蹤任務的狀態,從而及時發現問題并恢復正常,減少損失。
?
?
?
對于一個實時任務而言,執行效率是非常關鍵的,這意味著不可能考慮用非常復雜的方式來實現任務監控,即使這樣可以做的比較完善,同時監控代碼本身也會引入一些異常,這就要求監控程序必須簡單可靠,能夠發現大多數問題,并能及時處理。
一個可能的簡單實現。
我們對每個任務加上一個監控的"殼",調度程序調用這個"殼"來完成對具體任務的引導和監控,相當于每個任務具有自治能力。這樣做的好處有:
- 分散控制。不需要修改調度主體程序,不增加調度過程的復雜度;
- 控制靈活,安全性高。對于不同任務可定義不同控制方式和控制參數,封裝在任務內部,靈活性好,對個別任務控制代碼的修改不會影響其他任務,即任務控制實現松藕合,避免錯誤擴散。適合團隊開發;
- 維護簡單,升級方便。對于新的任務加入也無需改變原來調度程序的結構,只需修改任務表及相關參數,這樣在性能提高的同時也簡化了軟件升級過程中的代碼維護量。
?
?
每個線程理論上有四種狀態:
new | 線程對象已經創建,但尚未啟動,不可運行 |
runnable | 一旦有時間分片機制有空閑的CPU周期,線程立即開始運行 |
dead | 從run()方法退出后,一個線程即消亡 |
blocked | 線程可運行,但有某種東西阻礙了它。調度機制不給它分配任何CPU時間,直到它進入runnable狀態 |
在實際操作中,為了便于描述,我們重新規定了線程的狀態:
Actived | 線程已被激活,處于運行狀態 |
Sleeping | 線程完成一個特定任務后退出,進入休眠狀態 |
Dead | 線程運行過程中發生異常,終止運行,處于死亡狀態 |
根據上述理論,我們只需要對Actived狀態的線程進行監控,也只有對Actived狀態監控才有意義,這是對監控模塊做出邏輯簡化。那么如何實現監控模塊對具體任務的監控呢?
對具體任務的監控方式有多種,根據任務的不同,需要采用不同的監控代碼,但是在結構上基本相同。這和類的重載概念有點相似。本文附有一個簡單的例子。
A任務每秒執行一個簡單的代數運算 j = 1/ i,并打印結果。我們故意在其中設置了一個異常陷阱,使得執行過程中出現一次被0除的算術異常,下面結合這個例子講述監控原理。
為了監控A,我們設計了一個監控線程M。M中定義了一些關鍵邏輯變量:
Keepchecking | 持續監控標志 |
laststatus | 保存上次監控狀態 |
maydeadtimes | 監控線程可能死亡的計數器 |
maydeadtimeout | 定義判斷線程死亡的邊界條件 |
deadtimes | 監控線程死亡次數的計數器 |
deadtimeout | 定義判斷線程不正常的邊界條件 |
為了適應監控,在A任務中相應增加一些可以被監控的狀態和行為:
dead | 死狀態標志 |
dead = !dead; | 改變狀態 |
整個監控就是圍繞這些狀態標志和行為展開的。A任務定期修改自己的dead標志,dead是一個布爾變量,理論上只要A沒有死,那么dead肯定是周期變化的(和心跳概念差不多),M需要做的就是監控dead的變化。為了避免一些偶然因素導致的誤判,我們在M中設置了一些計數器和邊界值(maydeadtimes和maydeadtimeout),當M發現A的可能死亡次數超過一定限制后,判斷A已死亡,不在繼續等待了,作為實時處理,首先注銷A的實例,然后重新啟動A(和我們計算機死機的復位很像),然后進入新一輪監控。
如果是因為系統偶然因素導致A死亡,那么在隨后的新的任務啟動過程中一般可以順利完成。但是萬一由于環境參數改變或軟件升級存在版本缺陷,A可能始終會產生異常,那么M是否需要耐心地監控下去呢?一個形象的例子是:如果你連續3次開機都失敗,你是否會懷疑機器有問題?當然,你會,那么M也應該會。
為了對A任務重復多次死亡有一個統計,M中又引入了另外對計數器和邊界值(deadtimes和deadtimeout),和你開計算機的過程一樣,如果連續n次都發現A有問題,可以基本肯定不是由于偶然因素引起的,需要對A的代碼或系統的環境進行檢查。M會發出告警,通知必須要對A進行審查了,然后清空A,自己自動安全退出。如果在核心調度程序中設置一個標志接受M們的告警,就可以有足夠理由終止其他任務的執行。可以看見,在A任務發生異常期間,M承擔了核心調度程序的維護功能。特別是當任務數量比較多的情況,核心調度程序只能采用排隊方式處理任務異常,而且由于處理異常的復雜程度不同,無法保證對多任務異常的實時處理。
還要考慮正常情況下A和M的關系。核心調度程序通過M啟動A任務后,M處于持續監控狀態,當A正常結束任務后,A需要通知M結束監控,這樣,當A進入休眠狀態后,M也不會占用內存空間,提高了系統資源的利用率。
通過以上描述,可以看到,上述監控思想具有清晰的概念和可操作性,占用資源少,為保證系統連續穩定運行創造了條件。
具體代碼實現附后。
運行結果如下:
?
異常情況 | 正常情況 |
i=-3: status=true
M read A status = true i=-2: status=false M read A status = false i=-1: status=true M read A status = true A become Exception! M read A status = true M read A status = true M read A status = true A is deaded! M is restarting A! ____________________________ i=-3: status=false M read A status = false i=-2: status=true M read A status = true i=-1: status=false M read A status = false A become Exception! M read A status = false M read A status = false M read A status = false A is deaded! M is restarting A! ____________________________ i=-3: status=true M read A status = true i=-2: status=false M read A status = false i=-1: status=true M read A status = true A become Exception! M read A status = true M read A status = true M read A status = true Alert! A is unstable, M will stop it (結束) |
i=1: status=true
M read A status = true i=2: status=false M read A status = false i=3: status=true M read A status = true i=4: status=false M read A status = false i=5: status=true M read A status = true A is Ending M M read A status = true (結束) |
?
![]() package safethread; // 核心調度程序 public class mythread { public mythread() { } public static void main(String[] args) { M m = new M(); } } // A 任務線程 class A extends Thread { public static boolean dead = false; M m; A(M m){ m = m; start(); } public void run(){ try{ for(int i=-3;i<= 5;i++){ int j=1/i; // 人為設置過程中陷阱 dead = !dead; // 活動狀態 System.out.println("i=" + i + ": status=" + dead); try{ sleep(2000); } catch(InterruptedException ie){ System.out.println("A is Interrupted!"); } } m.Keepchecking = false; //A 正常結束后關閉監控線程 System.out.println("A is Ending M"); } catch(Exception e){ System.out.println("A become Exception!"); } } } // 監控線程 class M extends Thread{ public static boolean Keepchecking = true; // 持續監控標志 boolean laststatus; //保存上次監控狀態 int maydeadtimes = 0; //監控線程可能死亡的計數器 int maydeadtimeout = 3;//定義判斷線程死亡的邊界條件 int deadtimes = 0; //監控線程死亡次數的計數器 int deadtimeout = 3; //定義判斷線程不正常的邊界條件 A a; M(){start();} public void run(){ schedule(); while(Keepchecking){ laststatus = a.dead; try{ sleep(2000); } catch(InterruptedException e){ System.out.println("M is Interrupted!"); } System.out.println("M read A status = " + a.dead); if(laststatus == a.dead ){ if(++maydeadtimes >= maydeadtimeout){ if(++deadtimes >= deadtimeout){ System.out.println("Alert! A is unstable, M will stop it"); a = null; break; } else{ System.out.println( "A is deaded!"); schedule(); System.out.println("M is restarting A!\n____________________________\n"); } } } else{ maydeadtimes = 0; } } } private void schedule(){ A a = new A(this); } }? |
|
?
?
通過給制定任務線程增加監控線程,可以很好地解決實時多任務環境下的安全監控問題,同時避免了核心調度線程事務過分復雜的問題。實踐證明,該方法復雜度小,占用資源少,運行可靠,適合復雜條件下的多任務環境。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
