科技 · 實用指南
Python queue.join() 一直卡住?稽核未完成任務與 worker 存活
區分佇列為空與未完成任務帳本,找出已退出消費者,正確處理哨兵及 Python 3.13+ 關閉語意。

queue.join() 等待的不是容器看起來為空,而是未完成任務計數歸零。每次 put 加一,每次 task_done 減一;worker 取走最後一項後若異常、取消或提前返回,empty() 仍可能為真。
每次成功 get 後都應在 finally 中恰好呼叫一次 task_done,並同時監控 worker 與 join。哨兵也須計數;立即關閉可能在工作未完成時解鎖,不能當成成功。
1. 證明哪個計數卡住
記錄每次 put、get、task_done 的任務號、worker 與單調時間,以 put 減確認對帳;empty 只描述容器。
不要清空私有容器或改 unfinished_tasks,這會造成假完成或負計數。
2. 讓確認在例外下恰好一次
get 成功後以 try/finally 包住處理並在 finally 呼叫 task_done;get 未返回時不可確認。
確認與業務成功分開記錄,避免重複 task_done 觸發 ValueError。
3. 同時觀察 worker 與 join
asyncio 要同時等待 join 與 worker;worker 先結束就讀取並傳播例外。
執行緒模式保留 worker 引用與停止訊號;逾時只是診斷,不是完成。
4. 對帳哨兵並選擇關閉
哨兵仍是排入項目,退出前必須確認;普通工作後再放正確數量。
shutdown(False) 保留正常 join 語意;shutdown(True) 可放棄工作,必須明確標示。
5. 用故障時序驗收
測試正常排空、例外、取消、哨兵遺漏、worker 提前退出與兩種關閉。
證明每項有終態、關閉後無新 put、worker 已停止,放棄項未被標成完成。
