原文链接
CCR以新颖的方式使用了C# 2.0语言的迭代器功能:程序员可以用顺序的方式来编写代码,迭代返回(yielding)CCR仲裁器或者其他的CCR Task,而不需要使用delegates来嵌套异步行为(也叫做回调)。多步的异步逻辑也可以被写到一个迭代方法里,从而极大的提高代码的可读性,维护异步行为,并且由于迭代返回不会阻塞操作系统线程,所以可以伸缩到百万级别的未决操作。
例18.
void
StartIterator()
{
//
create an IterativeTask instance and schedule it
Arbiter.Activate(_taskQueue,
Arbiter.FromIteratorHandler(IteratorExample)
);
}
///
<summary>
///
Iterator method scheduled by the CCR
///
</summary>
IEnumerator
<
ITask
>
IteratorExample()
{
//
accumulator variable
int
totalSum
=
0
;
var portInt
=
new
Port
<
int
>
();
//
using CCR iterators we can write traditional loops
//
and still yield to asynchronous I/O !
for
(
int
i
=
0
; i
<
10
; i
++
)
{
//
post current iteration value to a port
portInt.Post(i);
//
yield until the receive is satisifed. No thread is blocked
yield
return
portInt.Receive();
//
retrieve value using simple assignment
totalSum
+=
portInt;
}
Console.WriteLine(
"
Total:
"
+
totalSum);
}
例18中,StartIterator方法是用Arbiter类来从一个迭代delegate创建任务,然后用Arbiter.Activate提交任务给调度。第二个方法是迭代方法,迭代方法是指使用yield return或者yield break C#语句来控制执行的方法。迭代方法和普通C#方法的区别是返回值:
IEnumerator
<
ITask
>
IteratorExample()
{
这个方法的返回值表明了这是一个在CCR ITask实例上的C#迭代器
yield
return
portInt.Receive(); {
上面的yield return语句返回控制权给CCR调度器。它也返回一个由调用portInt.Receive扩展方法创建的ReceiverTask对象(ITask接口的实例)。CCR调度器
重要:永远不要再迭代器中返回永久接收器。在上面的yield语句中,Arbiter.Receive的persist参数是false(第一个参数)。如果接收器是永久的,yield永远不会被满足,所以迭代器会停止迭代,永远不会处理yield后的下一个语句。
通过本地变量隐式的传递参数
将CCR与CLR迭代器一起使用的好处来自于两个C#的语言特性:
- 匿名方法-我们已经在前面的例子中使用过这个特性,用来定义内联的delegate。
- 编译器“捕获”所有在匿名方法中引用的迭代方法里面的局部变量。这使得delegate可以使用这些定义在父方法中的局部变量。总是运行在其他线程中的delegate也可以将结果返回给父迭代器,而不需要任何的显式参数传递。
//
retrieve value using simple assignment
totalSum
+=
portInt;
上面是yield之后的下一个方法。
部分返回到协调原语
例19
void
StartIterator2()
{
Port
<
string
>
portString
=
new
Port
<
string
>
();
Arbiter.Activate(
_taskQueue,
Arbiter.ReceiveWithIterator(
false
, portString, StringIteratorHandler)
);
}
IEnumerator
<
ITask
>
StringIteratorHandler(
string
item)
{
Console.WriteLine(item);
yield
break
;
}
上面的这个例子展示了如何指定一个迭代方法作为一个接收操作的结果(the outcome of a receive operation)。到现在为之,我们用于在仲裁器被满足时执行的方法都是传统的方法(methods)。Arbiter类包含一些方法,它们需要一个迭代器delegate(The Arbiter class contains methods that expect an iterator delegate for all the major coordination primitives (JoinReceiver, MultipleItemGather, Choice, Receiver, etc) giving the programmer the choice between regular methods or iterator methods for every CCR coordination primitive.)。仲裁器的实现依赖ITask而不是用户delegate,ITask隐藏了用户delegate的类型,所以仲裁器可以适应迭代或者非迭代方法。
例20.
IEnumerator
<
ITask
>
IteratorWithChoice()
{
//
create a service instance
ServicePort servicePort
=
ServiceWithInterleave.Create(_taskQueue);
//
send an update request
UpdateState updateRequest
=
new
UpdateState();
updateRequest.State
=
"
Iterator step 1
"
;
servicePort.Post(updateRequest);
string
result
=
null
;
//
wait for either outcome before continuing
yield
return
Arbiter.Choice(
updateRequest.ResponsePort,
response
=>
result
=
response,
ex
=>
Console.WriteLine(ex)
);
//
if the failure branch of the choice executed, the result will be null
//
and we will terminate the iteration
if
(result
==
null
)
yield
break
;
//
print result from first request
Console.WriteLine(
"
UpdateState response:
"
+
result);
//
now issue a get request
GetState
get
=
new
GetState();
servicePort.Post(
get
);
//
wait for EITHER outcome
yield
return
Arbiter.Choice(
get
.ResponsePort,
delegate
(
string
response) { result
=
response; },
delegate
(Exception ex) { Console.WriteLine(ex); }
);
//
print result from second request
Console.WriteLine(
"
GetState response:
"
+
result);
}
迭代器嵌套
当一个迭代方法变得太大以至于难以很轻松的维护时,可以把它的功能分解到更小的迭代方法。CCR调度器可以检测何时一个迭代器完成了他的所有步骤。这意味着迭代器方法到达了一个yield中断或者结束了最后一步的迭代执行。
例21
IEnumerator
<
ITask
>
ParentIteratorMethod()
{
Console.WriteLine(
"
Yielding to another iterator that will execute N asynchronous steps
"
);
yield
return
new
IterativeTask
<
int
>
(
10
, ChildIteratorMethod);
Console.WriteLine(
"
Child iterator completed
"
);
}
IEnumerator
<
ITask
>
ChildIteratorMethod(
int
count)
{
Port
<
int
>
portInt
=
new
Port
<
int
>
();
for
(
int
i
=
0
; i
<
count; i
++
)
{
portInt.Post(i);
//
short form of receive that leaves item in port
yield
return
portInt.Receive();
//
implicit operator extracts item from port
int
result
=
portInt;
}
}
例21通过简单的创建一个新的IterativeTask对象并且部分返回(yielding)到它来展示了嵌套迭代。
- 父迭代方法部分返回执行到一个新的IterativeTask实例。
- CCR把IterativeTask实例调度到当前迭代器使用的dispatcher queue实例上。Arbiter.FromIteratorHandler可以被用来替换显式创建一个IterativeTask。
- 子迭代方法在一个dispatcher线程中执行,并且部分返回10次到一个接收操作。一个循环被用来展示迭代器在异步操作外包裹循环是多么的容易和具有可读性。它也展示了父迭代器可以部分返回到任意复杂的子迭代器而不许知道子迭代中有多少异步环节。
值得注意的是,你可以通过调用Arbiter.ExecuteToCompletion方法部分返回到一个常规任务。
如果一个异常在嵌套迭代器中执行处理器时被抛出,父迭代器将仍然被调用。异常隐式终止迭代并且CCR会适当的释放他们。如果因果选择器存在,异常将会被投递到异常port,然后迭代器将被释放。