2014-01-29 97 views
2

我在其run方法結束時再次過一個可運行的情況下,自我安排其自身:主線程:可以運行的程序可以被搶佔嗎?

private class MyRunnable implements Runnable { 
     private volatile boolean cancelled = false; 
     private Handler handler; 

     public MyRunnable(Handler h){ 
      handler = h; 
     } 


     @Override 
     public void run(){ 
      //Do stuff 
      if(!cancelled){ 
       //Preemtion possible here? 
       handler.postDelayed(this, 1000); 
      } 
     } 

     public void selfStart(){ 
      cancelled = false; 
      handler.removeCallbacks(this); 
      handler.post(this); 
     } 

     public void selfCancel(){ 
      cancelled = true; 
      handler.removeCallbacks(this); 
     } 
    } 

可運行的計劃是在主線程中調用從selfStart活動的onStart第一

與此同時,可以從活動的onStop以及廣播接收器中從外部取消可運行子系統(呼叫selfCancel)。

AFAIK Runnable.runActivity.onStopBroadcastReceiver.onReceive運行在同一個線程(主要的),所以乍一看我認爲不會有線程安全問題。

但有時看起來,可運行程序正在其run調用的中間被搶佔,然後它從活動或接收器中被取消,然後恢復並重新安排自己。

這可能嗎?


UPDATE:
我會試着更好地解釋這個問題。上面顯示的類旨在定期在主線程中運行任務。在「do stuff」註釋中,實際上有一些代碼用傳遞給MyRunnable構造函數的值更新TextView。該活動取消當前可運行並在收到特定意圖時啓動新運行。儘管當前可運行的程序總是被要求在創建新程序之前自行取消,但有時它會與新程序一起運行,所以文本視圖顯示交替的值。這不是預期的行爲。

我想如果runnable當前正在主線程中運行,它會運行直到完成,然後其他runnables或事件處理程序將被帶出隊列並在需要時執行,但是沒有掛起的事件或runnable可能會「一半執行」。

有兩種在這都涉及到問題的主線程運行的任務:

  • R1:本MyRunnable自調度任務。運行,然後自我再次延遲1秒。
  • R2:請求取消當前的MyRunnable實例並創建新的R1'的事件處理程序。這些隨機發生,只執行一次。

我已經考慮了兩種情況。第一個:

  1. R1已經在主線程中運行。
  2. R2到達並且在主線程中排隊。
  3. R1完成運行並重新發布。
  4. R2運行並刪除R1的回調。
  5. R1應該永不再運行。

,第二個:

  1. R1未運行,但計劃。
  2. R2到達並刪除R1的回調。
  3. R1應該永不再運行。

從理論上講,如果沒有預設,並且只有一個線程,那麼主線程中有時會出現兩個R1?

+0

如果您僅將Runnable發佈到一個Looper,那麼您描述的內容是不可能的 – pskink

+0

您如何知道可標記的行上的runnable已被佔用?它是否有可能執行handler.post(this),然後結束它的當前運行? – user3118604

+0

@ user3118604我不認爲它被搶佔,這是唯一可能的解釋,我可以找到時,有一個單一的線程。但聽起來很牽強,我不想相信這甚至是可能的。 –

回答

1

由於您在selfStart或selfCancel上沒有同步,所以完全有可能。

在您的run語句檢查了cancelled的值後,可以在單獨的線程上調用未註釋的註釋,selfCancel。然後MyRunnable會再次獲得一個運行的調用,這會在它被取消時立即結束。

+0

儘管如果涉及到多個線程,情況會如此,但除非您解釋爲什麼某些代碼將在除主/ UI線程以外的線程上運行,否則對於所提問題來說這並不是一個有意義的答案。 –

+0

沒有單獨的線程。所有調度或取消都在主線程中調用。在這些方法上同步不會改變太多,因爲'selfStart'只在應用程序啓動時調用一次。有問題的方法是'selfCancel'和'run'。我認爲'selfCancel'在run標誌檢查中被調用。 –

+0

@MisterSmith - 如果只涉及一個線程,這是不可能的。爲什麼不添加用於每種方法的線程日誌記錄? –

1

我的建議是將//Do stuff移到取消的支票中。

這避免了比賽,不管有關運行哪個線程的假設。

@Override 
    public void run(){ 
     if(!cancelled){ 
      //Do stuff 
      handler.post(this); 
     } 
    } 

一般可維護性,嘗試寫,無論是它的作品正在運行的線程的正確的代碼。你永遠不知道什麼時候有人會在其他線程上調用selfCancel()後來認爲它可以,當你認爲他們不會那樣做的時候。

+0

在我的特殊情況下,取消後「東西」是否運行並不重要,但無論如何感謝您的建議。沒有人,永遠不會從另一個線程調用'selfCancel',這是保證:)它在文檔中明確提到。 –

+0

大聲笑。因爲將它放入文檔意味着未來的開發人員肯定會閱讀文檔。 –

+0

可能不會有任何其他未來的開發者,現在我是唯一一個擁有此功能的開發者。但是,在發佈該評論後,我改變了主意,如果方法未從主線程調用,我現在拋出異常。 –

0

就像其他人所說的那樣,一個線程中不能搶佔runnable。我也認爲這個想法是荒謬的。因爲提出這個廢話而感到羞恥。

runnables本身沒有任何問題。他們在活動的onStart中啓動,並從活動收到的意圖中或活動的onStop中取消。這是問題的根源:假設onStartonStop將以可預測的順序運行。有時當回到活動時,第二個onStart在第一個活動的onStop之前被執行。兩個任務正在運行,事情搞糟了,第一個任務從未終止。

確保在沒有事先解決問題的情況下不會啓動任務。

相關問題