原文链接
线程套间约束
当与某些遗留的Windows组件,特别是COM对象交互时需要特定的线程套件策略。甚至最近的框架,例如.net WinForms,也需要在运行WinForm的线程上应用单线程套间(SingleThreadedApartment)策略。
CCR可以轻松的承载STA组件或者与它互操作:组件应该创建一个只有一个线程的CCR Dispatcher实例,并且在Dispatcher的构造函数中指定线程套间策略。DispatcherQueue实例就可以在与需要和遗留代码交互的地方使用这个dispatcher来激活处理函数(activating handlers)。这些处理函数就可以安全的访问COM或者WinForm对象,同时对其他的CCR组件隐藏它们的STA关系,以便其他CCR组件可以简单的投递元素到常规的CCR ports而不需要关心port上的处理函数使用的是什么dispatcher。
CCR WinFrom适配库(Ccr.Adapters.Winforms.dll)是一个在CCR中运行.net WinForm的便利方法。
与应用的主线程协调
CCR软件组件经常在一个CLR应用的上下文中中执行,例如一个独立执行程序。.net运行时使用一个操作系统线程启动一个程序,当线程退出时终止程序。由于CCR应用是异步、并发的,所以它们在没有消息被发送时处于非激活状态,并且几乎不会阻塞任何线程。CCR Dispatcher将保持线程在一种高效的休眠状态,但是如果其它的线程被作为后台线程创建,应用程序将会退出,那么CCR还在执行。
一个与CLR启动的同步世界接口的常见模式是使用System.Threading.AutoResetEvent来阻塞应用的主线程,直到CCR应用完成。AutoResetEvent事件可以被任何CCR处理函数触发。
例25.
void
InteropBlockingExample()
{
//
create OS event used for signalling
AutoResetEvent signal
=
new
AutoResetEvent(
false
);
//
schedule a CCR task that will execute in parallel with the rest of
//
this method
Arbiter.Activate(
_taskQueue,
new
Task
<
AutoResetEvent
>
(signal, SomeTask)
);
//
block main application thread form exiting
signal.WaitOne();
}
void
ThrottlingExample()
{
int
maximumDepth
=
10
;
Dispatcher dispatcher
=
new
Dispatcher(
0
,
"
throttling example
"
);
DispatcherQueue depthThrottledQueue
=
new
DispatcherQueue(
"
ConstrainQueueDepthDiscard
"
,
dispatcher,
TaskExecutionPolicy.ConstrainQueueDepthDiscardTasks,
maximumDepth);
Port
<
int
>
intPort
=
new
Port
<
int
>
();
Arbiter.Activate(depthThrottledQueue,
Arbiter.Receive(
true
, intPort,
delegate
(
int
i)
{
//
only some items will be received since throttling will discard most of them
Console.WriteLine(i);
})
);
//
post items as fast as possible so that the depth policy is activated and discards
//
all the oldest items in the dispatcher queue
for
(
int
i
=
0
; i
<
maximumDepth
*
100000
; i
++
)
{
intPort.Post(i);
}
}
///
<summary>
///
Handler that executes in parallel with main application thread
///
</summary>
///
<param name="signal"></param>
void
SomeTask(AutoResetEvent signal)
{
try
{
for
(
int
i
=
0
; i
<
1000000
; i
++
)
{
int
k
=
i
*
i
/
10
;
}
}
finally
{
//
done, signal main application thread
signal.Set();
}
}
在上面的例子中,我们演示了一个使用操作系统事件来阻塞应用程序主线程的小例子。
简化.NET异步编程模式
CCR可以用于任何实现了异步变成模型模式的.net类型。它实际上极大的简化了异步模式,并且当用于C#时,完全不再需要delegate和接续传递(continuation passing)。CCR迭带器调度支持允许你直接返回并继续(yield)挂起的I/O操作并且实现的易读的代码和过去只能在同步代码中实现的模式。
例27.
IEnumerator
<
ITask
>
CcrReadEnumerator(
string
filename)
{
var resultPort
=
new
Port
<
IAsyncResult
>
();
//
stage 1: open file and start the asynchronous operation
using
(FileStream fs
=
new
FileStream(filename,
FileMode.Open, FileAccess.Read, FileShare.Read,
8192
, FileOptions.Asynchronous))
{
Byte[] data
=
new
Byte[fs.Length];
fs.BeginRead(data,
0
, data.Length, resultPort.Post,
null
);
//
stage 2: suspend execution until operation is complete
yield
return
Arbiter.Receive(
false
, resultPort,
delegate
{ });
//
stage 3: retrieve result of operation just by assigned variable to CCR port
var ar
=
(IAsyncResult)resultPort;
try
{ Int32 bytesRead
=
fs.EndRead(ar); }
catch
{
//
handle I/O exception
}
ProcessData(data);
}
}
例子27演示了CCR如何使用文件流的异步编程模型BeginRead/EndRead方法而又不需要传递delegate。取而代之的是,我们提供一个CCR Port的Post方法,所以异步结果被直接投递到一个CCR Port。代码返回并继续在一个port上进行接受操作。yield return语句允许我们编写逻辑上顺序的代码而不需要使用一个操作系统线程。代码保留了异步的可伸缩性和overlapped操作,但是读起来就像是同步、顺序的代码。
例28
///
<summary>
///
Read from one stream into a Http request stream, asynchronously
///
</summary>
public
virtual
IEnumerator
<
ITask
>
UploadHttpStream(
string
contentUrl,
Stream contentStream, PortSet
<
HttpStatusCode, Exception
>
resultPort)
{
//
Create HTTP request
HttpWebRequest webRequest
=
(HttpWebRequest)WebRequest.Create(contentUrl);
webRequest.Method
=
"
POST
"
;
HttpStatusCode status
=
HttpStatusCode.OK;
Exception ex
=
null
;
using
(Stream requestStream
=
webRequest.GetRequestStream())
{
byte
[] readBuffer
=
new
byte
[
1024
];
int
bytesRead
=
-
1
;
//
With CCR and iterators you can do loops and asynchronous I/O
do
{
//
use CCR stream adapter (or a custom APM adapter) to schedule operation
var ioResultPort
=
StreamAdapter.Read(contentStream, readBuffer,
0
,
1024
);
//
yield to result (success or failure)
yield
return
(Choice)ioResultPort;
//
check for error
ex
=
ioResultPort;
if
(ex
!=
null
)
{
resultPort.Post(ex);
//
exit on error
yield
break
;
}
bytesRead
=
ioResultPort;
var writeResultPort
=
StreamAdapter.Write(requestStream, readBuffer,
0
, bytesRead);
//
yield to write operation
yield
return
(Choice)writeResultPort;
//
check for write error
ex
=
writeResultPort;
if
(ex
!=
null
)
{
resultPort.Post(ex);
yield
break
;
}
}
while
(bytesRead
>
0
);
requestStream.Flush();
}
//
Use built-in CCR adapter for reading HTTP response
var webResultPort
=
WebRequestAdapter.ReadResponse(webRequest, _taskQueue);
//
yield to web response operation
yield
return
(Choice)webResultPort;
//
check for any exceptions
GetErrorDetails((Exception)webResultPort,
out
status);
resultPort.Post(status);
}