TCL线程无法同时运行问题:仅首对线程并行其余串行原因排查
问题核心原因
所有非并行的问题本质都是TCL脚本立即求值规则和TCL线程为独立解释器的特性导致的,具体分两类场景说明:
1. CCC/DDD线程串行的原因
你创建t3、t4时使用的写法是thread::create [threadprocSync "CCC" 10],TCL中[]包裹的内容会在当前上下文(主线程)立即执行:
- 主线程先完整执行
threadprocSync "CCC" 10,输出所有CCC日志,再将这个proc的返回值(空字符串)传给thread::create创建空线程 - 等CCC全部输出完成后才会执行
t4的创建逻辑,同样先在主线程输出所有DDD日志再创建空线程 - 对比能并行的t1/t2,你是用
{}包裹了执行逻辑,没有在主线程立即求值,而是直接把代码块传给了子线程执行,所以能实现并行
另外补充:TCL每个线程都是独立的Tcl解释器,主线程定义的threadprocSync默认不会同步到子线程,哪怕你不触发立即求值,直接把proc名传给子线程也会报“命令不存在”的错误。
2. EEE/FFF线程串行的原因
除了和上面完全一致的[]立即求值问题(主线程先跑完threadprocAsync输出所有EEE/FFF再创建空线程)之外,还有两个额外错误:
thread::send -async $t [outproc $msg $i]中[outproc $msg $i]同样会在当前线程立即执行,不会作为脚本传给目标线程tWorker- 你在主线程定义的
outproc不存在于tWorker的独立解释器中,就算正确把命令传给tWorker也会报错
修正方案
要实现多线程并行,需要满足两个核心要求:
- 传给
thread::create的是待执行的代码块,不要在主线程提前求值 - 子线程执行用到的所有proc、变量都需要在子线程上下文定义,或者通过序列化方式传递
以CCC/DDD线程为例,正确写法参考:
# 把proc定义和执行逻辑都放在大括号里,传给子线程执行 set t3 [thread::create { proc threadprocSync {msg n} { for {set i 0} {$i < $n} {incr i} { puts "$msg $i" after 50 } } threadprocSync "CCC" 10 }] puts "Created thread t3" set t4 [thread::create { proc threadprocSync {msg n} { for {set i 0} {$i < $n} {incr i} { puts "$msg $i" after 50 } } threadprocSync "DDD" 10 }] puts "Created thread t4"
以EEE/FFF的异步发送场景为例,正确写法参考:
# 先给worker线程定义好outproc set tWorker [thread::create -preserved { proc outproc {msg i} { puts "$msg $i" } }] puts "Created thread tWorker" # 用subst控制变量替换逻辑,避免命令提前求值,同时传递tWorker变量到子线程 set t5 [thread::create [subst -nocommands -novariables { proc threadprocAsync {t msg n} { for {set i 0} {$i < $n} {incr i} { # 用list包裹要发送的命令,避免提前求值 thread::send -async $t [list outproc $msg $i] after 50 } } threadprocAsync $tWorker "EEE" 10 }]] puts "Created thread t5" set t6 [thread::create [subst -nocommands -novariables { proc threadprocAsync {t msg n} { for {set i 0} {$i < $n} {incr i} { thread::send -async $t [list outproc $msg $i] after 50 } } threadprocAsync $tWorker "FFF" 10 }]] puts "Created thread t6"
内容的提问来源于stack exchange,提问作者PavelH
相关产品推荐
相关产品推荐

