Design Pattern: Worker Thread 模式

本文介绍了WorkerThread模式在请求管理上的应用,它结合了ProducerConsumer模式和Command模式的特点。通过具体的Java实现示例,展示了如何利用Channel类来管理多个WorkerThread,并通过Request类定义具体的工作任务。

Worker Thread模式在Request的管理上像是 Producer Consumer 模式,在Request的行为上像是 Command 模式

Producer Consumer模式专注于Product的生产与消费,至于Product被消费时是作何处理,则不在它的讨论范围之中。 如果您的Product是一个Request,消费者取得Request之后,执行Request中指定的请求方法,也就是使用Command模式,并且您的Request缓冲区还管理了Consumer,就有Worker Thread模式的意思了。在Sequence Diagram上,可以看出Worker Thread同时展现了Producer Consumer模式与Command模式:

利用Java实现的一个Channel类如下所示:

  • Channel.java
import java.util.LinkedList; 

public class Channel { 
    private LinkedList requests; 
    private WorkerThread[] workerThreads; 

    public Channel(int threadNumber) { 
        requests = new LinkedList(); 
        workerThreads = new WorkerThread[threadNumber]; 
        for(int i = 0; i < workerThreads.size(); i++) { 
            workerThreads[i] = new WorkerThread(); 
           workerThreads[i].start(); 
        } 
    } 

    public synchronized void putRequest(Request request) { 
        while(requests.size() >= 2) { // 容量限制为 2 
            try { 
                wait(); 
            } 
            catch(InterruptedException e) {} 
        } 

        requests.addLast(request); 
        notifyAll(); 
    } 
    
    public synchronized Request getProduct() { 
        while(requests.size() <= 0) { 
            try { 
                wait(); 
            } 
            catch(InterruptedException e) {} 
        } 

        Request request = (Request) requests.removeFirst(); 
        notifyAll(); 
      
        return request;
    } 
} 


Request类与WorkerThread类之间采的Command模式:

  • Request.java
public class Request() { 
    // .... 

    public void execute() { 
        // do some work.... 
    } 
} 

 

  • WorkerThread.java
public class WorkerThread extends Thread { 
    // ... 

    public void run() { 
        while(true) { 
            Request request = channel.getRequest(); 
            request.execute(); 
        } 
    } 
} 


就行为上,WorkerThread就是有请求来了就作,如果没有请求,则所有的WorkerThread就等待,直到有新的工作进来而通知它们,取得请求的WorkerThread要作的工作,就直接定义在execute()中。

<think> 我们正在讨论VPP框架中`vlib_worker_thread_barrier_sync_int`函数导致的worker线程死锁问题。根据引用[1],VPP使用带信号和超时机制的自旋锁进行多线程同步。当超过`BARRIER_SYNC_TIMEOUT`时间(默认5秒)时,系统会认为发生死锁并abort。 死锁常见原因及解决方案如下: ### 1. **节点运行时更新导致死锁**(最常见) 引用[2]指出:当添加新节点时,主线程会设置`need_vlib_worker_thread_node_runtime_update=1`,在释放屏障(barrier)前触发worker线程重建运行环境。若此时worker线程因阻塞无法响应,则导致死锁。 **解决方案**: ```c // 在调用barrier sync前检查节点更新标志 if (vlib_global_main.need_vlib_worker_thread_node_runtime_update) { vlib_worker_thread_node_runtime_update(); // 提前处理更新 } vlib_worker_thread_barrier_sync (vm); // 再执行同步 ``` ### 2. **资源竞争死锁** 当多个线程以不同顺序获取锁时,可能形成环形等待。例如: ``` Worker1: 持有锁A -> 申请锁B Worker2: 持有锁B -> 申请锁A ``` **解决方案**: - 使用锁层级(lock hierarchy)规定加锁顺序 - 将`BARRIER_SYNC_TIMEOUT`从5秒调整为更高值(临时方案): ```bash # 启动VPP前设置环境变量 export VLIB_BARRIER_SYNC_TIMEOUT=20 ``` - 检查锁使用:确保所有临界区代码简洁且无嵌套锁 ### 3. **中断处理冲突** 若在barrier同步期间处理中断,可能破坏同步状态。 **解决方案**: - 在barrier同步前后禁用中断: ```c uword intr_flags = clib_interrupt_save(); vlib_worker_thread_barrier_sync (vm); clib_interrupt_restore (intr_flags); ``` ### 4. **内存不足导致阻塞** worker线程在同步期间申请内存失败,陷入无限重试。 **解决方案**: - 增大VPP内存池: ```bash # startup.conf配置 dpdk { socket-mem 2048,2048 } ``` - 检查内存泄漏:使用`show memory`命令监控内存使用 ### 调试步骤 1. **捕获abort日志**: ```bash gdb -ex "set pagination off" -ex "thread apply all bt" -batch -p $(pidof vpp) ``` 2. **检查死锁位置**: - 若所有worker线程均阻塞在`vlib_worker_thread_barrier_sync_int`,则验证原因1或2 - 若某线程卡在内存分配函数(如`clib_mem_alloc`),则验证原因4 3. **使用动态追踪**: ```bash # 跟踪barrier超时事件 sudo perf probe -x /usr/bin/vpp vlib_worker_thread_barrier_timeout sudo perf record -e probe_vpp:vlib_worker_thread_barrier_timeout -p $(pidof vpp) ``` > **注意**:修改同步超时仅为临时方案,需结合具体堆栈定位根本原因[^1][^2]。 §§相关问题§§ 1. VPP框架中如何处理多线程间的同步? 2. 如何诊断和解决VPP中的内存泄漏问题? 3. 在VPP中调整线程屏障超时参数有哪些影响? </think> ### 解决 `vlib_worker_thread_barrier_sync_int` Worker Thread 死锁问题的方案 根据您提供的引用和VPP框架的同步机制,死锁通常发生在工作线程同步屏障(barrier)时线程无法正常退出等待状态。以下是根本原因和解决方案: --- #### **死锁根本原因** 1. **同步超时** VPP的同步屏障机制(`vlib_worker_thread_barrier_sync_int`)有超时限制(默认5秒)。若工作线程未在规定时间内释放屏障,会触发`BARRIER_TIMEOUT`错误[^1]。 2. **节点运行时更新冲突** 当主线程在同步过程中更新节点运行时(如调用`vlib_worker_thread_node_runtime_update`),会设置`need_vlib_worker_thread_node_runtime_update=1`。若worker线程因此阻塞在同步点,会导致死锁[^2]。 3. **资源竞争** 工作线程在等待屏障释放时,可能因锁嵌套(如自旋锁+条件变量)或资源争用形成环形等待。 --- #### **解决方案** ##### 1. 检查同步超时配置 ```c // 修改同步超时阈值(单位:秒) #define BARRIER_SYNC_TIMEOUT 10 // 从默认5秒调整为10秒 ``` > 通过环境变量动态调整: > ```bash > export VLIB_BARRIER_SYNC_TIMEOUT=10 > ``` ##### 2. 修复节点更新逻辑 确保在**同步屏障外**处理节点运行时更新: ```c void node_update_handler() { if (vlib_global_main.need_node_runtime_update) { // 先释放屏障再更新 vlib_worker_thread_barrier_release(vm); vlib_worker_thread_node_runtime_update(); vlib_worker_thread_barrier_sync(vm); // 重新同步 } } ``` ##### 3. 避免锁嵌套 确保工作线程在进入同步屏障时**不持有其他锁**: ```c void worker_thread() { // 错误示例:在持有锁时进入同步 spin_lock(&my_lock); vlib_worker_thread_barrier_sync(vm); // 可能死锁 spin_unlock(&my_lock); // 正确做法:先释放再同步 spin_lock(&my_lock); /* 临界区操作 */ spin_unlock(&my_lock); // 先释放锁 vlib_worker_thread_barrier_sync(vm); // 再进入同步 } ``` ##### 4. 添加死锁检测 ```c // 在vlib_worker_thread_barrier_sync_int中增加调试信息 if (wait_time > BARRIER_SYNC_TIMEOUT) { clib_warning("Thread %u deadlocked at barrier! Stack trace:", thread_index); vlib_unix_backtrace(); // 打印堆栈 } ``` --- #### **验证步骤** 1. **启用调试日志** ```bash vppctl set logging class barrier debug ``` 2. **检查线程状态** 使用VPP命令查看工作线程状态: ```bash vppctl show threads ``` 死锁的线程会显示`BARRIER_WAIT`状态且超时。 3. **核心转储分析** 当超时触发abort时,用GDB分析核心转储: ```bash gdb -ex "thread apply all bt" -ex q /usr/bin/vpp core.dump ``` 重点检查阻塞在`vlib_worker_thread_barrier_sync_int`的线程。 --- ### **根本解决思路** VPP的同步屏障本质是**自旋锁+条件等待**的混合实现[^1]。死锁通常由以下场景触发: 1. 主线程在同步期间修改了worker线程依赖的资源(如节点图) 2. Worker线程在等待屏障时未释放其持有的其他锁 3. 同步超时阈值设置过短 通过隔离**资源更新**与**同步操作**、增加超时容忍度、避免锁嵌套,可彻底解决此类死锁。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值