添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
Skip to content

Threads

Stability: 1 - Experiment

threads 模块提供了多线程支持,可以启动新线程来运行脚本。

脚本主线程会等待所有子线程执行完成后才停止执行,因此如果子线程中有死循环,请在必要的时候调用 exit() 来直接停止脚本或 threads.shutDownAll() 来停止所有子线程。

通过 threads.start() 启动的所有线程会在脚本被强制停止时自动停止。

由于 JavaScript 自身没有多线程的支持,因此您可能会遇到意料之外的问题。

threads.start(action)

  • action <Function> 要在新线程执行的函数
  • 返回 Thread

启动一个新线程并执行 action。

例如:

通过该函数返回的 Thread 对象可以获取该线程的状态,控制该线程的运行中。例如:

更多信息参见 Thread

threads.shutDownAll()

停止所有通过 threads.start() 启动的子线程。

threads.currentThread()

返回当前线程。

threads.disposable()

新建一个 Disposable 对象,用于等待另一个线程的某个一次性结果。更多信息参见 线程通信 以及 Disposable

threads.atomic([initialValue])

  • initialValue <Number> 初始整数值,默认为 0
  • 返回 AtomicLong

新建一个整数原子变量。更多信息参见 线程安全 以及 AtomicLong

threads.lock()

新建一个可重入锁。更多信息参见 线程安全 以及 ReentrantLock

Thread

线程对象, threads.start() 返回的对象,用于获取和控制线程的状态,与其他线程交互等。

Thread 对象提供了和 timers 模块一样的 API,例如 setTimeout() , setInterval() 等,用于在该线程执行相应的定时回调,从而使线程之间可以直接交互。例如:

Thread.interrupt()

中断线程运行。

Thread.join([timeout])

  • timeout <Number> 等待时间,单位毫秒

等待线程执行完成。如果 timeout 为 0,则会一直等待直至该线程执行完成;否则最多等待 timeout 毫秒的时间。

例如:

isAlive()

  • 返回 <Boolean>

返回线程是否存活。如果线程仍未开始或已经结束,返回 false ; 如果线程已经开始或者正在运行中,返回 true

waitFor()

等待线程开始执行。调用 threads.start() 以后线程仍然需要一定时间才能开始执行,因此调用此函数会等待线程开始执行;如果线程已经处于执行状态则立即返回。

Thread.setTimeout(callback, delay[, ...args])

参见 timers.setTimeout()

区别在于, 该定时器会在该线程执行。如果当前线程仍未开始执行或已经执行结束,则抛出 IllegalStateException

Thread.setInterval(callback, delay[, ...args])

参见 timers.setInterval()

区别在于, 该定时器会在该线程执行。如果当前线程仍未开始执行或已经执行结束,则抛出 IllegalStateException

Thread.setImmediate(callback[, ...args])

参见 timers.setImmediate()

区别在于, 该定时器会在该线程执行。如果当前线程仍未开始执行或已经执行结束,则抛出 IllegalStateException

Thread.clearInterval(id)

参见 timers.clearInterval()

区别在于, 该定时器会在该线程执行。如果当前线程仍未开始执行或已经执行结束,则抛出 IllegalStateException

Thread.clearTimeout(id)

参见 timers.clearTimeout()

区别在于, 该定时器会在该线程执行。如果当前线程仍未开始执行或已经执行结束,则抛出 IllegalStateException

Thread.clearImmediate(id)

参见 timers.clearImmediate()

区别在于, 该定时器会在该线程执行。如果当前线程仍未开始执行或已经执行结束,则抛出 IllegalStateException

线程安全

线程安全问题是一个相对专业的编程问题,本章节只提供给有需要的用户。

引用维基百科的解释:

线程安全是编程中的术语,指某个函数、函数库在多线程环境中被调用时,能够正确地处理多个线程之间的共享变量,使程序功能正确完成。

在 Hamibot 中,线程间变量在符合 JavaScript 变量作用域规则的前提下是共享的,例如全局变量在所有线程都能访问,并且保证他们在所有线程的可见性。但是,不保证任何操作的原子性。例如经典的自增"i++"将不是原子性操作。

Rhino 和 Hamibot 提供了一些简单的设施来解决简单的线程安全问题,如锁 threads.lock() , 函数同步锁 sync() , 整数原子变量 threads.atomic() 等。

例如,对于多线程共享下的整数的自增操作(自增操作会导致问题,是因为自增操作实际上为 i = i + 1 ,也就是先读取 i 的值, 把他加 1, 再赋值给 i, 如果两个线程同时进行自增操作,可能出现 i 的值只增加了 1 的情况),应该使用 threads.atomic() 函数来新建一个整数原子变量,或者使用锁 threads.lock() 来保证操作的原子性,或者用 sync() 来增加同步锁。

线程不安全的代码如下:

此段代码运行后打开日志,可以看到日志中有重复的值出现。

使用 threads.atomic() 的线程安全的代码如下:

或者:

或者:

另外,数组 Array 不是线程安全的,如果有这种复杂的需求,请用 Android 和 Java 相关 API 来实现。例如 CopyOnWriteList , Vector 等都是代替数组的线程安全的类,用于不同的场景。例如:

但很明显的是,这些类不像数组那样简便易用,也不能使用诸如 slice() 之类的方便的函数。在未来可能会加入线程安全的数组来解决这个问题。当然您也可以为每个数组的操作加锁来解决线程安全问题:

sync(func)

  • func <Function> 函数
  • 返回 <Function>

给函数 func 加上同步锁并作为一个新函数返回。

线程通信

Hamibot 提供了一些简单的设施来支持简单的线程通信。 threads.disposable() 用于一个线程等待另一个线程的(一次性)结果,同时 Lock.newCondition() 提供了 Condition 对象用于一般的线程通信(await, signal)。另外, events 模块也可以用于线程通信,通过指定 EventEmiiter 的回调执行的线程来实现。

使用 threads.disposable() 可以简单地等待和获取某个线程的执行结果。例如要等待某个线程计算"1+.....+10000":

如果上述代码用 Condition 实现:

如果上诉代码用 events 模块实现: