上一篇曾說道我從2018年開始創業,是因為機緣巧合之下開發了一款電話機器人程式,我想嘗試能否做好一個小而美的軟體,就索性成立公司去做了。大家對接到許多推銷電話都感到厭煩,所以並不指望這個產品有大的發展,只希望它在適當的場景下能夠發揮一點的作用。在開發和優化這個軟體的過程中遇到了一些比較有意思技術問題 ...
上一篇曾說道我從2018年開始創業,是因為機緣巧合之下開發了一款電話機器人程式,我想嘗試能否做好一個小而美的軟體,就索性成立公司去做了。大家對接到許多推銷電話都感到厭煩,所以並不指望這個產品有大的發展,只希望它在適當的場景下能夠發揮一點的作用。在開發和優化這個軟體的過程中遇到了一些比較有意思技術問題,我儘量寫出來分享。
我們知道軟體開發中的有些情況需要用多線程來處理,而多線程會增加程式的複雜度,容易造成一些不可預知的問題,怎麼辦呢?我總結出一個方法就是把多線程里的事情,儘可能又回歸到單線程來處理,許多問題就迎刃而解了。
那麼問題來了:哪些事情應該回歸到單線程來處理?具體該如何實現?我就用兩個具體的案例來說一說。
下圖是這個WinForm程式在運行過程中的界面,圖中的4個小視窗展示了4個電話機器人的工作狀況。
案例一
每個機器人要求是並行工作的,所以需要同時開4個線程,每個線程通過一個任務隊列來驅動機器人工作。 每一通電話開始呼叫、結束呼叫時都要把數據Update到本地SQLite資料庫的一個表中。起初這個Update語句就是在每個機器人自己的線程中運行的,在測試時沒出現問題。突然有幾次在客戶那裡運行過程中出現了數據表損壞!查了資料發現SQLite並不是線程安全的。也就是說SQLite在多線程環境下,Update同一個表的數據可能會出錯。
這個嚴重的問題需要馬上解決,怎麼辦?把Update語句移到一個單獨的線程里來運行。每個機器人線程需要更新數據時,只需要把數據放入到一個隊列里,在另外開啟的一個線程負責把隊列里的數據Update到數據表。問題解決了,但需要註意原先是同步更新數據,現在變成了非同步更新。
案例二
機器人在打電話過程中可以提醒話務員人工介入,如下圖所示。當需要人工介入時會彈出提示框,話務員點接聽後,就由人工代替機器人與對方溝通。
這個提示框應該在機器人自己的線程中通過Invoke主線程的某個方法彈出來嗎?或者高級一點的寫法是給機器人增加一個事件(event),在掛載到該事件的函數中彈出提示框?
我的方法是機器人只維護一個當前是否需要人工介入的變數,機器人自己不作任何處理,而是由主線程的一個定時器輪詢當前是否有機器人處於需要人工介入的狀態,如果有則彈出提示框。
這樣一個本來在多線程環境下產生的事情,由單線程來處理了。雖然又多了一個輪詢,方法有點土,很多人不屑於這麼做,但我記得曾經有人說過,輪詢就是把臟活留給自己,美好留給別人。
好了,總結一下,把事情從多線程環境中移到單線程中來處理可能是一個簡化問題的方法。具體方案可以考慮藉助於隊列、輪詢來實現。