# 线程

最近事件调用的执行流被定义为 当前线程; 例如包括热键, SetTimer 子程序, 自定义菜单项以及 GUI 事件. 当前线程 可以在它自身子程序或由那个子程序调用其他子程序中执行命令.

尽管 AutoHotkey 实际并不支持多线程, 但它模拟了多线程的某些特性: 如果启动了第二个线程, 例如在前一个热键仍在执行时又按了另一个热键, 那么 当前线程 会被中断(暂时停止) 来让新的线程成为 当前 线程. 如果在第二个线程还在运行时又启动了第三个线程, 那么第二个和第一个线程都会进入休眠状态, 依此类推.

当 当前线程 结束时, 最近中断的线程将恢复执行, 依此类推, 直到最后所有的线程都结束. 当线程被恢复时, 它的设置例如 ErrorLevel 和 SendMode 会自动恢复到中断前的状态; 换句话说, 中断不会给线程带来副作用(不过活动窗口可能会变化而产生影响).

注意: KeyHistory 命令/菜单项会显示处于中断状态的线程数量, 而 ListHotkeys 命令/菜单项则显示哪些热键拥有线程.

一个脚本可以同时含有多个 MsgBox, InputBox, FileSelectFile 和 FileSelectFolder 对话框. 这是在前一个线程已经显示对话框后启动新线程(通过热键, 定时子程序, 自定义菜单项等) 实现的.

默认情况下, 子程序仍在运行的热键或热字串不能再次启动. 使用 #MaxThreadsPerHotkey 可以改变此特性.

相关: Thread 命令用于设置线程的优先级或中断性.

## 线程优先级

优先级低于 当前线程 的任何线程(热键, 定时子程序, 自定义菜单项等) 不能中断当前线程. 在这种时候, 这样的计时器不会运行, 且用户试图创建线程(例如按下热键或 GUI 按钮) 会没有效果, 也不会被缓存起来. 由于这个原因, 通常在设计时最好让高优先级的线程快速结束, 或使用 Critical 而不将它们设置成高优先级.

默认的优先级为 0. 所有线程使用默认的优先级, 不过可以使用下列的其中一种方法改变:

- 通过 SetTimer 给定时子程序指定优先级.
- 通过 Hotkey 命令给热键指定优先级.
- 热字串在定义时或通过 #Hotstring 可以指定优先级.
- 通过 Menu 命令给自定义菜单项指定优先级.
- 通过 Thread 命令给 当前线程 设置自己的优先级.

不论 当前线程 的优先级如何, OnExit 线程(如果有) 被调用时总是会运行.

## 线程可中断性

对于大多数类型的事件, 只有在当前线程 可中断 的情况下, 才允许启动新线程. 一个线程可以因为一些原因而 不可中断, 包括:

- 线程已被标记为 critical. Critical 可能已经被线程本身或从自动执行段调用.
- 线程运行时间不够长, 无法满足线程中断设置的成为可中断的条件, 如 Thread Interrupt 设置的.
- 脚本的一个菜单正在显示(如托盘图标菜单或菜单栏).
- Send (最常见的是由于 SetKeyDelay), WinActivate, 或 Clipboard 操作正在执行延迟.
- OnExit 线程正在执行.
- 由于达到了 #MaxHotkeysPerInterval 的限制, 或者由于激活键盘或鼠标钩子的问题(非常罕见), 正在显示一个警告对话框.

### 不可中断线程的特性

与高优先级线程不同, 在关键线程中产生的事件不会被丢弃. 例如, 在当前线程为关键线程时用户按下了热键, 那么此热键会被缓冲到当前线程结束或成为非关键线程时才作为新的线程启动.

关键线程也会被紧急事件中断. 紧急事件包括: 1) OnExit 回调; 2) 监听消息号小于 0x0312 的任何 OnMessage() 函数(或被这些消息触发的回调); 和 3) 由紧急线程自身间接触发的任何回调(例如通过 SendMessage 或 DllCall). 要避免被这些事件中断, 可以临时禁用这些函数.

当显示 MsgBox 或其他对话框时, critical 会变得可中断. 但与 Thread Interrupt 不同的是, 在用户解除对话框后, 线程又变成了 critical(因此不可中断).

dftg