这篇文章根据我实测的两个 H 版多线程示例整理,重点不是“开线程越多越快”,而是把 线程怎么启动、函数怎么调用、对象怎么传、共享数组怎么写 这几件事讲清楚。相关命令可以先看 H 版帮助文档里的 AhkThread()、CriticalObject(),以及总入口 AHK_H 帮助文档。
先看最小线程
AHK_H 的 AhkThread() 是在当前进程里创建新的 AutoHotkey 线程。它和 Run 一个新脚本不是一回事:线程之间可以共享进程内对象,也可以通过 H 版对象方法互相调用函数。
#Requires AutoHotkey v1.1
#NoEnv
SetBatchLines, -1
t1 := AhkThread("MsgBox, 子线程 1")
t2 := AhkThread("MsgBox, 子线程 2")
MsgBox, 主线程也在运行。点掉这个窗口后,再看两个子线程窗口。
ahkthread_free(t1), t1 := ""
ahkthread_free(t2), t2 := ""
对象怎么传
实测示例里一个很容易踩坑的点是:把对象地址传给新线程时,&obj 后面最好拼一个空字符串,让它稳定按字符串参数传进去;在线程里再用 Object(ptr) 还原。不要在调用参数里直接写 Object(obj) 当中介。
threadCode =
(
#Persistent
ReadObj(ptr) {
obj := Object(ptr)
MsgBox, % "子线程读取:" obj["a", "b"]
obj["a", "b"] := "str2"
}
)
obj := []
obj["a", "b"] := "str1"
t := AhkThread(threadCode)
t.ahkFunction("ReadObj", &obj "")
MsgBox, % "主线程看到:" obj["a", "b"]
共享数组不要硬怼
多个线程都往同一个数组里频繁 Push(),锁竞争会很明显。实测更推荐:每个线程先写自己的临时数组,最后再把整块结果 Push 到共享对象。这样锁次数少,结构也更清楚。
threadCode =
(
#Persistent
WriteChunk(sharedPtr, count) {
shared := CriticalObject(sharedPtr)
temp := []
Loop %count%
temp[A_Index] := "Value " A_Index
shared.Push(temp)
ExitApp
}
)
threadCount := 4
itemCount := 40000
shared := CriticalObject()
Loop %threadCount% {
th%A_Index% := AhkThread(threadCode)
th%A_Index%.ahkPostFunction("WriteChunk", &shared "", itemCount // threadCount)
}
Loop {
ready := ""
Loop %threadCount%
ready .= th%A_Index%.ahkReady()
if !ready
break
Sleep, 50
}
MsgBox, % "线程块数:" shared.MaxIndex() "`n第一块数量:" shared[1].MaxIndex()
ahkFunction 与 ahkPostFunction
ahkFunction()会等待线程里的函数执行完,适合需要返回值的场景。它更像同步调用。ahkPostFunction()发出调用后马上返回,适合异步任务。它的返回值主要表示调用是否投递成功,不适合当成业务结果。- 线程代码里如果要反复接收调用,通常需要
#Persistent。否则线程可能已经退出,后面的函数调用就会失败。 - 传参优先按字符串、数字理解。对象、数组这类复杂数据要用指针和
Object()还原。
threadCode =
(
#Persistent
GetText(name) {
return "来自子线程的返回值:" name
}
DoWork(name) {
MsgBox, % "后台任务收到:" name
}
)
t := AhkThread(threadCode)
; 同步调用:会等待,并拿到返回值
MsgBox, % t.ahkFunction("GetText", "task-1")
; 异步调用:马上返回,业务结果要用其它方式保存
ret := t.ahkPostFunction("DoWork", "task-2")
MsgBox, % "投递结果:" ret
实战建议
- 小任务不要强行多线程,创建线程、同步数据也有成本。
- CPU 密集型任务可以按核心数切块,不要无脑开几十个线程。
- 共享对象适合传状态、传结果,不适合所有线程都高频修改同一个字符串。
- 异步任务要设计完成标志,例如
obj._t1_status := 1,不要只靠固定Sleep猜时间。 - 测试脚本里可以暴力关进程,但正式脚本应优先
ExitApp、ahkTerminate()、ahkthread_free()这类可控释放方式。
声明:站内资源为整理优化好的代码上传分享与学习研究,如果是开源代码基本都会标明出处,方便大家扩展学习路径。请不要恶意搬运,破坏站长辛苦整理维护的劳动成果。本站为爱好者分享站点,所有内容不作为商业行为。如若本站上传内容侵犯了原著者的合法权益,请联系我们进行删除下架。

评论(0)