在現代多核 Linux 伺服器環境下,設計高併發網路服務的核心挑戰在於:如何充分利用 CPU 核心、避免執行緒爭用、簡化同步複雜度。本文提煉出經實戰驗證的「Event Loop per Thread + Thread Pool」混合模型,並建立 IPC 與同步原語的選用準則。
核心職責簡化為:「收封包 → 計算 → 回封包」。
單執行緒下,non-blocking I/O + I/O 多工 是高效能標準:
while (!done) {
int timeout = max(1000, getNextTimer());
int n = epoll_wait(epfd, events, maxevents, timeout);
if (n < 0) handle_error();
process_timers();
if (n > 0) process_io_events(events, n);
}
代表實作:lighttpd、nginx、libevent/libev、Java NIO (Netty/Mina)、Twisted (Python)。優點:無鎖、無上下文切換、邏輯單一。缺點:無法利用多核心進行 CPU 運算。
// 每個 I/O 執行緒運行獨立的 Reactor
void io_thread_func(EventLoop* loop) {
while (!quit) {
loop->run_once(); // epoll_wait + 處理 timer + I/O 事件
}
}
accept 後,根據負載策略分配給某個 I/O 執行緒的 loop(注冊 channel)。blocking_queue<function<void()>> taskQueue;
void worker_thread() {
while (!quit) {
auto task = taskQueue.take(); // 阻塞等待
try { task(); }
catch (...) { log_error(); }
}
}
// 啟動 N 個計算執行緒
for (int i = 0; i < num_compute_threads; ++i)
create_thread(worker_thread);
// 提交計算任務
taskQueue.post(bind(&BusinessLogic::heavy_calc, this, data));
亦可用 blocking_queue<T> 實作生產者-消費者資料管線。
| 方案 | 跨主機 | 生命週期管理 | 除錯可觀測性 | 適用性 |
|---|---|---|---|---|
| TCP | ✅ 原生支援 | OS 自動回收 fd,進程死即連線斷 | tcpdump/Wireshark 完整重現 | ✅ 首選 |
| Unix Domain Socket | ❌ 單機 | 需手動清理 socket 檔 | 同 TCP | 高效本機通訊 |
| 共享記憶體 | ❌ 無法跨機 | 進程崩潰留下髒資料,需複雜恢復 | 難以記錄重現 | 極致延遲敏感 |
| Pipe/FIFO | ❌ 單機、需父子關係 | 同共享記憶體 | 一般 | 簡單單向通訊 |
核心論點:為了微小效能增益引入共享記憶體,會大幅增加程式碼複雜度、測試負擔、故障恢復難度。TCP 的字節流模型配合 Protocol Buffers 序列化,已足夠應付絕多數業務場景。
BlockingQueue、CountDownLatch、Future/Promise,而非直接操作 mutex/condition variable。pthread_mutex_t 預設),RAII 封裝 MutexLock/MutexLockGuard。while (cond) wait() 模式。shared_ptr + copy-on-write 常能替代。class MutexLock {
pthread_mutex_t mutex_;
public:
MutexLock() { pthread_mutex_init(&mutex_, nullptr); }
~MutexLock() { pthread_mutex_destroy(&mutex_); }
void lock() { pthread_mutex_lock(&mutex_); }
void unlock() { pthread_mutex_unlock(&mutex_); }
pthread_mutex_t* get() { return &mutex_; } // 僅供 Condition 使用
};
class MutexLockGuard {
MutexLock& mutex_;
public:
explicit MutexLockGuard(MutexLock& m) : mutex_(m) { mutex_.lock(); }
~MutexLockGuard() { mutex_.unlock(); }
};
#define MutexLockGuard(x) static_assert(false, "missing variable name")
class Condition {
MutexLock& mutex_;
pthread_cond_t cond_;
public:
explicit Condition(MutexLock& m) : mutex_(m) { pthread_cond_init(&cond_, nullptr); }
~Condition() { pthread_cond_destroy(&cond_); }
void wait() { pthread_cond_wait(&cond_, mutex_.get()); }
void notify() { pthread_cond_signal(&cond_); }
void notifyAll() { pthread_cond_broadcast(&cond_); }
};
template<typename T>
class Singleton {
static pthread_once_t once_;
static T* value_;
public:
static T& instance() {
pthread_once(&once_, []{ value_ = new T(); });
return *value_;
}
};
無雙重檢查鎖定、無 memory barrier 複雜度、編譯器保證初始化原子性。
| 層面 | 決策 | 理由 |
|---|---|---|
| I/O 模型 | non-blocking + epoll (Reactor) | 高併發、低延遲、單執行緒無鎖 |
| 執行緒架構 | Event Loop per I/O Thread + Compute Thread Pool | 充分利用多核、職責分離、負載可調度 |
| IPC | TCP Sockets | 跨主機、OS 自動清理、可觀測、協定標準 |
| 同步原語 | Mutex + Condition Variable (RAII) → 高層 Queue/Latch | 正確性優先、工具鏈支援、避免 lock-free 陷阱 |
| 序列化 | Protocol Buffers | Schema 演進、跨語言、高效二進制 |
多執行緒程式設計的核心不是「用盡所有技巧」,而是「用最少、最正確的工具解決問題」。
先建立 正確、可維護、可觀測 的架構骨架,再針對測量證實的熱點進行局部優化。
下一篇將探討「多執行緒反模式」:sleep 濫用、鎖順序死結、物件生命週期競態等實戰大坑。