一切为了并行: MS Axum语言教程 <三>

[b]域与状态共享[/b]

消息传递是种优秀的沟通机制,但它要消息中的数据必须可以被深拷贝或者不可变。但有时代理间共享数据会让程序更有效率,也更简单,当然,要提供一种安全的方式。

这就是设计出域的目的。域存在的目的是允许一组代理安全的共享状态,同时将该状态与其他人等隔离开来。

[b]在代理间共享状态[/b]

象普通类,域可以包含字段与方法,但更重要的,域定义可以包含代理的定义,在域内定义的代理可以存取域的字段。如
[code]
domain Chatroom {
private string m_Topic;
private int m_UserCount;
reader agent User : channel UserCommunication {
// ...
}
writer agent Administrator : channel AdminCommunication{
// ...
}
}
[/code]

这里声明的域Chatroom包含两个代理 User和Administrator。chatroom让许多不同的用户互相交换信息,(或在管理员间),并且读取域的状态,如m_Topic,但不能改变这些字段。administrator是唯一能改变域状态的代理。让我们更仔细的探讨一下reader和writer的区别。

[b]Reader与Writer的语义[/b]

Reader和Writer锁相当普遍地用同步上,它允许在多个读用户或者一个写用户间存取一个共享资源。这保证了读用户总是看到资源的一致状态,且两个写用户不会彼此干扰。

Axum中的代理遵循同样的原则。声明成"reader"的代理只允许读域的状态,而写代理可以读写这个状态。既不是reader也不是writer的代理只能读取域内不可变状态。

在代理内可以用parent关键字来存取所在域的字段和方法。上面的User代理可以这样读取域的状态:

[code]
reader agent User : channel UserCommunication
{
public User() {
if( parent.m_Topic.Contains("Axum") ) {
// Start talking to other users in this chatroom
}
else {
// Do nothing and leave
}
}
}
[/code]

如果这个代理试图改变域的状态,编译则无法通过。

与"this"类似,parent是可以省略的。关键字parent只在需要消除同名的域成员与本地变量的歧义时才是必须的,否则使用parent只是个人习惯问题。

同样,代理也被限制改变系统的全局状态。例如,代理不能写一个静态字段,或者调用可以改变(甚至只是可能)静态字段的方法。

当你开始用Axum编写程序的时候,你会注意到一些代码(特别是第三方的类库)不能被编译除非你把Agent标记为writer,或把类标记为isolated(后面会详述)。虽然某些情况下这样做事可以的,但要记住同一域内的writer代理是无法并发执行的。只有一个域且所有代理都是writer的应用只是个单线程。

Axum有个“后门”让你能执行那些会改变共享状态的代码 -- 使用unsafe关键字。正如它的名字一样,使用unsafe就意味着代码不安全,你应该只在你确定代码不修改任何共享状态或这些修改是良性的。让程序慢点但无错误总比快而到处是错强多了。
[code]
reader agent User : channel UserCommunication {
private WriteLog(string str) {
unsafe { Logger.WriteLine(str); }
}
}
[/code]

这儿我们想要使用unsafe如果第三方的类Logger使用它自己的同步,或者我们不担心记录文件可能出现的行错位。

[b]宿主代理 Hosting Agent[/b]

回忆第一个Adder例子,通道Adder的实例是这样被创建的:
[code]
var adder = AdderAgent.CreateInNewDomain();
[/code]

这样创建了由代理AdderAgent所实现的,通道Adder的两个ends(记得么 implement end和using end),并返回通道Adder的using end.和名字所示一样,CreateInNewDomain方法同时也创建了域,并让AdderAgent的parent参考指向它。

每次都这样创建一个新的域实例意味代理间无法共享状态,因为他们的parent参考指向了不同的域实例。显然,我们需要一种方法来解决实例化代理并与域相关联的问题。

Hosting是种将代理类型与域的特殊实例关联的方法。 通过保有代理的类型,可以如下:
[code]
Host<AdderAgent>("my adder");
[/code]

当用户请求保有在"my adder"地址的"Adder"服务时,代理AdderAgent将被实例化,关联到当前域,并返回通道Adder的using end。保有代理类型的地址可以是任何表达式,只要支持“等于”比较。这儿我们选择字符,因为它是描述性的。

因为Host方法将代理与当前域相关联(它是个域方法),它只能在域的上下文中被执行,而不能在域之外声明的agent中。

现在,代理可以这样被实例化:
[code]
var adder = new Adder("my adder");
[/code]

这可以理解为“创建通道channel的using end,该通道由保有在地址“my adder"的服务所实现”。

注意:用户不了解这个代理的类型,他所要关心的只是通道的类型和实现该通道的服务的地址。

为了更好理解代理作为服务的想法,想一下一个给个数字会返回相应的斐波那契数列的通道。不象加法,只有一种有意义的方式,斐波那契额数列可以用很多种不同的计算方法,高效的低效的。

同一个Fibonacci通道可以由两个不同的代理来实现,让我们称他们为FibonacciSlow和FibonacciFast,这两个代理类型被保有在不同的地址上:
[code]
Host<FibonacciSlow>("slow");
Host<FibonacciFast>("fast");
[/code]
现在根据用户的需要,她可以在这个或那个代理上实现Fibonacci通道。
[code]
Fibonacci fibChannel;
if(gotTimeToSpare) {
fibChannel = new Fibonacci("slow");
}
else {
fibChannel = new Fibonacci("fast");
}
var request = fibChannel::Number <-- 42;
[/code]

当代理提供的服务不再需要的时候,可以用evicted方法将其从域内清除出去。
[code]
Evict("slow");
Evict("fast");
[/code]

记住从域内清除一个代理类型不会影响已经创建的该代理类型的实例,不过,无法再通过操作符new创建更多的该代理的实例。一旦该代理类型被清除了,试图创建它的实例会导致一个运行时异常。

版权所有,如有转载,请站内联系.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值