顯示具有 programming 標籤的文章。 顯示所有文章
顯示具有 programming 標籤的文章。 顯示所有文章

2013年2月11日 星期一

GUI 架構的考量

( 和 Scott 聊過後, 決定改寫《非同步程式新手心得: 為什麼要用單一 thread + task queue 的方式設計架構》的前半段。比較完整地描述 GUI 架構的入門心得。 )

GUI 需要低延遲的反應速度, 隨時能接收使用者輸入和並提供畫面回饋。這表示要在不互相阻檔的前提下應付三種工作:

  • 更新畫面
  • 偵測使用者輸入
  • 處理使用者輸入

甚至第四種情況:

  • 網路輸入

若是使用 multi-thread 處理上述情況 (比方說有三個 thread 分別專職處理前述三件工作), 必須留意 thread 之間有共享資料的情況。為了避免 race condition 必須使用 lock; 使用 lock 後又要擔心 dead lock。

使用 global lock 有可能成效不彰, 比方說更新畫面太久, 而沒有即時回饋使用者的輸入。另一方面, 切許多細小的 lock 處理各塊共享資料, 容易出錯造成 dead lock, 或誤以為有處理好卻仍存在 race condition。

反過來說, 若每個工作 (#1) 都切得很細, 執行時間不長, 但可能需要多個工作單元才能完成目標。這樣就能在一個 thread 內以類似 queue consumer 的方式執行全部的工作。有新的需求時, 放入工作到 queue 裡, 大致上可確保相依工作之間的執行順序 (#2)。由於全部工作都在同一 thread 執行, 自然不用擔心 dead lock 或 race condition。

於是可以看到, 像 Gtk、iOS、Android 的平台, 都將更新畫面、偵測事件和處理事件三者整合在 main thread 處理, 並且假設程式以 single thread 的方式執行 (#3)。

我不清楚其它平台的情況, iOS 有提供 NSURLRequest 應付常見的網路需求。讀寫資料和事件的 callback 也都在 main thread 內執行。

這麼一來, 框架開發者省事許多, 使用框架的開發者也可以安心地實作 callback。

備註

#1 可想像每個工作是一個函式, 在 iOS 或 Android 上, 整合這種「工作 queue」的概念到基本框架裡, 用起來很直覺, 希望盡量避免開發者從 thread 的角度去處理事情。

#2 不行的時候只好加入狀態變數了解目前該做什麼, 至少不用擔心狀態變數本身會有 race condition。

#3 若開發者有特別需求, 比方涉及檔案或網路 I/O, 或是需要大量 CPU 計算, 而無法在短時間內完成單一工作的話, 開發者需要自行另外處理。使用新的 thread 的話, 自然需要使用 lock 保護和 main thread 共享的資料, 但需要煩惱的範圍已降致最小。

2013年2月9日 星期六

非同步程式新手心得: 為什麼要用單一 thread + task queue 的方式設計架構

最近多了一些 GUI + 非同步IO 的開發經驗, 簡記一下心得。

GUI 架構背後的考量

  • 由於 GUI 需要低延遲的反應速度, 必須使用非同步的方式實作。
  • Gtk/Android/iOS 都在 main thread 裡處理畫面顯示和事件 callback 是有道理的。藉由架構保證所有 task (繪圖、偵測事件、事件 callback) 在同一 thread 內執行, 寫程式時不用擔心 race condition。反之, 若將繪圖、偵測事件、事件 callback 三者拆到兩個 threads 以上, 會衍生許多潛在問題。
  • 共享變數太雜時, 使用 global lock + multi-thread 無法提高太多效能, 失去 multi-thread 的意義。
  • 若依各別變數使用不同的 lock 容易寫出問題。
  • 在同一個 thread 內執行內全部的 task, 可以避免使用 lock, 相對省事許多。然後用戶有效能需求時, 針對有需求的部份另開 thread 加速, 減少需要使用 lock 的地方。

實作注意事項

  • 由於程式不是一路通到底, 不適合用以往的直線方式思考。
  • 盡量從環境狀態的角度了解如何處理當下的 task, 而減少保證 task 之間的執行順序。這樣寫起來比較簡單, 也會比較穩定。
  • 程式本身或註解要能表示清楚狀態變化對各 task 的影響。提供 debug flag 可以 log 各 task 執行順序, 或註解提及主要的和特別的 task flow, 應該有助於日後維護。
  • 若能留下產生每個 task 的 task 為何, 有助於日後在 debugger 內除錯。
  • 開始實作以前, 要想清楚可能的狀態變化, 以及留好協助維護的 debug code, 由 debug flag 可以開關。

在 Fedora 下裝 id-utils

Fedora 似乎因為執行檔撞名,而沒有提供 id-utils 的套件 ,但這是使用 gj 的必要套件,只好自己編。從官網抓好 tarball ,解開來編譯 (./configure && make)就是了。 但編譯後會遇到錯誤: ./stdio.h:10...