导读:CCR可以轻松的承载STA组件或者与它互操作:组件应该创建一个只有一个线程的CCR Dispatcher实例,并且在Dispatcher的构造函数中指定线程套间策略。DispatcherQueue实例就可以在与需要和遗留代码交互 的地方使用这个dispatcher来激活处理函数(activating handlers)。这些处理函数就可以安全的访问COM或者WinForm对象,同时对其他的CCR组件隐藏它们的STA关系,以便其他CCR组件可以 简单的投递元素到常规的CCR ports而不需要关心port上的处理函数使用的是什么dispatcher。
原文链接
线程套间约束
当与某些遗留的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.
{
// 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.
{
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);
}
}
/// 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);
}
参考
MSDN Magazine: Concurrent Affairs -- Concurrency and Coordination Runtime