域(Domain)
域(Domain) 是 GS 中的一个重要概念,它被用于保护 GS 的基本线程安全。
数据争用
在介绍域之前,需要先介绍多线程(并行)编程的一个基本问题,即数据争用(Data race)。 当多个线程同时访问一个共享的数据时,如果至少一个线程尝试修改数据,其他线程将可能读取到不完整、不一致的错误数据。
为了解决这类问题,不同的语言和平台有不同的设计,常见的方法包括:
- 锁(Lock):通过加锁的方式,确保同一时刻只有一个线程可以访问共享数据。
- 原子操作(Atomic operation):通过原子操作的方式,确保对数据的读写操作是不可分割的。
- 所有权模型(Ownership model):通过所有权的方式,确保数据只能被一个线程拥有,其他线程无法访问。
以上方法除了原子操作之外,都是通过限制对数据的访问权来避免数据争用的问题。在 GS 中,域(Domain) 也是用于同样的目的。
域的定义
在 GS 中,域是一个保护一组 对象 和数据的 "范围"(也可以说是一个锁),同一时刻只能有一个协程 在一个域上运行。
在 GS 中,任何 RW 的 引用容器类型 都不能直接从一个域传递到另一个域,这是因为这 些类型具有完全的内部可变性,一旦对此类型实例的读/写操作与其他写操作并行地发生了,可能会访问到损坏的内部结构而导致崩溃。如果将这 样的实例限制在一个域内,因为 同一时刻,一个域上只能有一个协程在运行,就避免了数据争用的问题。
需要注意的是,域只提供最基本的保护。当涉及到存在多个域的不同数据时,由于域(通常)只能持有一个域。对持有域的出让操作往往可能导致
重入问题。因此,对于复杂情况的安全保证,需要使用其他手段辅助,确保行为正确。
跨域规则
当需要访问另一个域中的数据时,需要执行跨域操作;
跨域发生时,协程当前的域会被释放(当然,也有特例),然后尝试进入目标域。
如下所示的一些常见情况可能发生跨域:
- 尝试以跨域方式调用一个对象的非 parallel 方法
- 以
call或call_domain的方式调用一个绑定有参数域的function - 调用了需要跨域的拓展方法(典型的例 子包括
new_object和load_static) - 发生异常,打印调用栈时
域的优先级
此特性已经被弃用
在域创建时,可以指定域的优先级:
domain d1 = domain.create("domain1", true, 10);
默认的优先级是 0,从低优先级的域进入更高优先级的域时,低优先级的域不会被释放;而是同时持有两个域。
这个特性已经被弃用,因为在实际开发中,同时需要考虑域和域的优先级会带来不小的心智负担。