通用線程規則

原文鏈接:
http://www.jetbrains.org/intellij/sdk/docs/basics/architectural_overview/general_threading_rules.html

插件應該使用ApplicationManager.getApplication().invokeLater()而不是標準的SwingUtilities.invokeLater()傳遞控制從后臺線程到事件分發線程。這個API允許調用指定modality state,即允許調用執行的模態對話框的堆棧。 傳遞ModalityState.NON_MODAL意味著在所有模態對話框關閉后執行操作。 傳遞ModalityState.stateForComponent()意味著可以在指定的組件(對話框的一部分)仍然可見時執行操作。

讀/寫鎖

通常情況下,*IntelliJ平臺 *中代碼相關的數據結構由一個單獨的讀寫鎖監控。這適用于PSI,VFS和項目根模型。

可以從任何線程讀取數據,從UI線程讀取數據不需要任何特殊的工作。但是,從任何其它線程的讀操作都需要使用ApplicationManager.getApplication().runReadAction()包裹,或者簡短一點使用ReadAction.run/compute

只允許從UI線程寫數據,寫操作必須被ApplicationManager.getApplication().runWriteAction()包裹,或者簡短一點使用WriteAction.run/compute。

除此之外,修改模型只允許從寫安全上下文中進行,包括用戶操作,安全上下文中的invokeLater調用(查閱下一部分)和事務(TransactionGuard.submitTransaction)。你可能無法從UI渲染器或SwingUtilities.invokeLater調用中修改PSI、VFS或項目模型。更多詳情請查閱TransactionGuard文檔。

你不能在讀寫操作之外訪問模型。相關對象不能保證在幾個連續的讀取操作之間存在。 所以根據經驗,每當你開始一個讀取操作前都應確認說操作的PSI / VFS /項目/模塊是否仍然有效。

invokeLater

插件應該使用ApplicationManager.getApplication().invokeLater()而不是標準的SwingUtilities.invokeLater()傳遞控制從后臺線程到事件分發線程。這個API允許調用指定模態狀態,即允許調用執行的模態對話框的堆棧。

  • 傳遞ModalityState.NON_MODAL意味著在所有模態對話框關閉后執行操作。注意這個狀態幾乎是不合適的,因為如果任何打開的(不相關的)項目顯示一個每個項目的模式對話框,該操作將在其關閉后執行。
  • 傳遞ModalityState.stateForComponent()意味著當頂層顯示的對話框包含指定組件或是指定組件父對話框之一時執行操作。
  • 如果沒有傳遞模態狀態,ModalityState.defaultModalityState()將會被使用。大多數情況下這是最佳選擇,這時來自UI線程的調用將使用當前模態狀態,并且對用ProgressManager啟動的后臺進程有特殊處理:來自此進程的invokeLater可能會運行在進程啟動時的相同對話框。
  • ModalityState.any()意味著runnable將會被盡快執行而不管模態對話框。請注意此時runnable中禁止修改PSI、VFS或項目模型。更多詳情請查閱TransactionGuard文檔。

如果你的UI線程活動需要訪問基于文件的索引(如正在進行項目范圍的PSI分析,解析引用等等),請使用DumbService#smartInvokeLater。這樣,你的活動將在所有可能的索引進程完成后運行。

防止UI凍結

后臺線程不應執行長時間的讀取操作。原因是如果UI線程需要寫操作(如用戶輸入某些東西),它必須盡快獲取控制權,否則在所有后臺線程都釋放讀操作之前UI將被凍結。

最著名的方法是每當即將發生寫操作時取消后臺讀取操作,稍后重新啟動后臺讀取操作。 編輯器高亮顯示,代碼補全,轉到類/文件等操作都是這樣的。 有兩種推薦的方法:

  • 如果你正在UI線程,插件一個ReadTask并將它傳到ProgressIndicatorUtils.schedule*方法中的其中一個。在onCanceled中, 如果活動需要重新啟動再次安排它執行。
  • 如果你已經在后臺線程,在循環中使用ProgressManager.getInstance().runInReadActionWithWriteActionPriority(),直到它通過或整個活動被廢棄。

在這兩種方法中,每個讀取操作的開始你應該總是檢查所用對象是否仍然有效,整個操作是否仍然有意義(即未被用戶取消,項目未被關閉等)。

如果你正在進行的活動必須訪問基于文件的索引(如正在進行項目范圍的PSI分析,解析引用等等),你應該重寫ReadTask#runBackgroundProcess并使用"smart-mode"讀取操作:DumbService.getInstance(project).runReadActionInSmartMode(() -> performInReadAction(indicator))

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,552評論 25 708
  • java 接口的意義-百度 規范、擴展、回調 抽象類的意義-樂視 為其子類提供一個公共的類型封裝子類中得重復內容定...
    交流電1582閱讀 2,280評論 0 11
  • 不管你承不承認 有些人就是過客 不論對于你或是其他人 有些事就只是經過 只會占據你人生總個記憶的一小片段 有些東西...
    解非兮竹閱讀 307評論 0 1
  • # 環境準備 1. 安裝git,自行百度 2. 安裝Node.js通過Homebrew或者從官網上下載安裝包的方式...
    心田祭思閱讀 284評論 0 0
  • 雨很大,女兒要自己步行去車站,后來貪聊天,時間不夠用,讓我送。 買來女兒愛吃的酸菜魚,吃完,想睡不敢睡,后來就到點...
    心若芷蘭閱讀 562評論 16 19