访问线程
使用 Future<T>
等待线程结束并获取返回值
在上面的例子中,新创建的线程会由于主线程结束而提前结束,在缺乏顺序保证的情况下,甚至可能会出现新创建的线程还来不及得到执行就退出了。可以通过 spawn
表达式的返回值,来等待线程执行结束。
spawn
表达式的返回类型是 Future<T>
,其中 T
是类型变元,其类型与 lambda 表达式的返回类型一致。当调用 Future<T>
的 get()
成员函数时,它将等待它的线程执行完成。
Future<T>
的原型声明如下:
__
public class Future\<T\> {
// Blocking the current thread, waiting for the result of the thread corresponding to the current Future object.
// If an exception occurs in the corresponding thread, the method will throw the exception.
public func get(): T
// Blocking the current thread, waiting for the result of the thread corresponding to the current Future object.
// If the corresponding thread has not completed execution within Duration, the method will throws TimeoutException.
// If `timeout` <= Duration.Zero, its behavior is the same as `get()`.
public func get(timeout: Duration): T
// Non-blocking method that immediately returns Option\<T\>.None if thread has not finished execution.
// Returns the computed result otherwise.
// If an exception occurs in the corresponding thread, the method will throw the exception.
public func tryGet(): Option\<T\>
}
下方示例代码演示了如何使用 Future<T>
在 main
中等待新创建的线程执行完成:
__
import std.sync.*
import std.time.*
main(): Int64 {
let fut: Future\<Unit\> = spawn { =>
println("New thread before sleeping")
sleep(100 * Duration.millisecond) // sleep for 100ms.
println("New thread after sleeping")
}
println("Main thread")
fut.get() // wait for the thread to finish.
return 0
}
调用 Future<T>
实例的 get()
会阻塞当前运行的线程,直到 Future<T>
实例所代表的线程运行结束。因此,上方示例有可能会输出类似如下内容:
__
New thread before sleeping
Main thread
New thread after sleeping
主线程在完成打印后会因为调用 get()
而等待新创建的线程执行结束。但主线程和新线程的打印顺序具有不确定性。
如果将 fut.get()
移动到主线程的打印之前,如下所示:
__
import std.sync.*
import std.time.*
main(): Int64 {
let fut: Future\<Unit\> = spawn { =>
println("New thread before sleeping")
sleep(100 * Duration.millisecond) // sleep for 100ms.
println("New thread after sleeping")
}
fut.get() // wait for the thread to finish.
println("Main thread")
return 0
}
主线程将等待新创建的线程执行完成,然后再执行打印,因此程序的输出将变得确定,如下所示:
__
New thread before sleeping
New thread after sleeping
Main thread
可见,get()
的调用位置会影响线程是否能同时运行。
Future<T>
除了可以用于阻塞等待线程执行结束以外,还可以获取线程执行的结果。如下是它提供的具体成员函数:
get(): T
:阻塞等待线程执行结束,并返回执行结果,如果该线程已经结束,则直接返回执行结果。
示例代码如下:
__
import std.sync.*
import std.time.*
main(): Int64 {
let fut: Future\<Int64\> = spawn {
sleep(Duration.second) // sleep for 1s.
return 1
}
try {
// wait for the thread to finish, and get the result.
let res: Int64 = fut.get()
println("result = ${res}")
} catch (_) {
println("oops")
}
return 0
}
输出结果如下:
__
result = 1
get(timeout: Duration): T
:阻塞等待该Future<T>
所代表的线程执行结束,并返回执行结果,当到达超时时间 timeout 时,如果该线程还没有执行结束,将会抛出异常 TimeoutException。如果timeout <= Duration.Zero
, 其行为与get()
相同。
示例代码如下:
__
main (): Int64 {
let fut = spawn {
sleep(Duration.second) // sleep for 1s.
return 1
}
// wait for the thread to finish, but only for 1ms.
try {
let res = fut.get(Duration.millisecond * 1)
println("result: ${res}")
} catch (_: TimeoutException) {
println("oops")
}
return 0
}
输出结果如下:
__
oops
访问线程属性
每个 Future<T>
对象都有一个对应的仓颉线程,以 Thread
对象为表示。Thread
类主要被用于访问线程的属性信息,例如线程标识等。需要注意的是,Thread
无法直接被实例化构造对象,仅能从 Future<T>
的 thread
成员属性获取对应的 Thread
对象,或是通过 Thread
的静态成员属性 currentThread
得到当前正在执行线程对应的 Thread
对象。
Thread
类的部分方法定义如下(完整的方法描述可参考《仓颉编程语言库 API》)。
__
class Thread {
... ...
// Get the currently running thread
static prop currentThread: Thread
// Get the unique identifier (represented as an integer) of the thread object
prop id: Int64
// Check whether the thread has any cancellation request
prop hasPendingCancellation: Bool
}
下列示例代码在创建新线程后分别通过两种方式获取线程标识。由于主线程和新线程获取的是同一个 Thread
对象,所以他们能够打印出相同的线程标识。
__
main (): Unit {
let fut = spawn {
println("Current thread id: ${Thread.currentThread.id}")
}
println("New thread id: ${fut.thread.id}")
fut.get()
}
输出结果如下:
__
New thread id: 1
Current thread id: 1