惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Tailwind CSS Blog
大猫的无限游戏
大猫的无限游戏
L
LINUX DO - 热门话题
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
雷峰网
雷峰网
aimingoo的专栏
aimingoo的专栏
博客园_首页
MongoDB | Blog
MongoDB | Blog
V
V2EX
GbyAI
GbyAI
量子位
Microsoft Azure Blog
Microsoft Azure Blog
有赞技术团队
有赞技术团队
G
Google Developers Blog
云风的 BLOG
云风的 BLOG
B
Blog
Microsoft Security Blog
Microsoft Security Blog
S
SegmentFault 最新的问题
O
OpenAI News
N
News and Events Feed by Topic
博客园 - Franky
爱范儿
爱范儿
Forbes - Security
Forbes - Security
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
V2EX - 技术
V2EX - 技术
Application and Cybersecurity Blog
Application and Cybersecurity Blog
N
News and Events Feed by Topic
N
News | PayPal Newsroom
Schneier on Security
Schneier on Security
Cloudbric
Cloudbric
Security Archives - TechRepublic
Security Archives - TechRepublic
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Recent Commits to openclaw:main
Recent Commits to openclaw:main
人人都是产品经理
人人都是产品经理
P
Privacy International News Feed
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
B
Blog RSS Feed
阮一峰的网络日志
阮一峰的网络日志
D
DataBreaches.Net
Last Week in AI
Last Week in AI
罗磊的独立博客
Spread Privacy
Spread Privacy
Recent Announcements
Recent Announcements
The Cloudflare Blog
Google DeepMind News
Google DeepMind News
AWS News Blog
AWS News Blog
The Register - Security
The Register - Security
Y
Y Combinator Blog
J
Java Code Geeks
I
Intezer

博客园 - iceboundrock

Introduction to the Reactive Extensions for JavaScript文章集合(收集贴) 美化VS2010在远程桌面/无法启动硬件加速渲染的机器下的字体显示 CCR源码分析-CCR架构 CCR源码分析-分析环境准备 CCR相关的Channel 9视频 DSS简介 也许你还不知道的一些关于App Domain的事 Common UNIX directories 几篇优化.net动态调用性能的文章 安装TFS 2008备注 Windbg点滴 MySQL Connector/Net 5.20安装后无法在VS2008中正常使用的问题 LAMP周边点滴 并行与并发编程学习心得 并发和协调运行时 其它并发方法 与非CCR代码互操作 迭代器 任务调度
失败处理
iceboundrock · 2007-12-17 · via 博客园 - iceboundrock

原文链接

传统的失败处理策略遵循如下模式:

  1. 对于同步方法调用,调用者检查一个或者多个返回值。被调用的方法使用调用者的执行上下文(通常执行上下文是一个线程)来运行。
  2. 使用结构化异常处理,调用者将同步方法调用包裹在try/catch/finally语句中,依赖catch{}块的执行来处理错误或者使用catch{}块加上显式的检查被调用方法的返回值。
  3. 依赖于被调用组件的各种补偿实现的事务,操作系统基础设置和通常很昂贵的机制来跟踪跨越多个线程的上下文。

上述所有方法都不能很容易的用于并发、异步执行,特别是前两种方法根本就不可能用于异步编程。方法在任意的上下文中执行,可能和调用者并行执行。除非有阻塞操作来收集异步操作的结果,阻塞一个在调用者上下文中的线程,失败或者成功都不能很容易的被检测。CLR中的异步编程模型使用Begin/End调用,引入了附加的复杂性,因为附加调用和无论操作结果如何都会被调用到的回调中的异常处理中都需要做失败处理。启动异步操作的代码不是进行失败处理的地方使得代码难以阅读。

CCR分两步解决失败处理的问题:

  1. 使用Choice和MultipleItemGather仲裁器处理显式或者本地异常。与迭代器合在一起,它们提供了一个类型安全并强壮的方法来处理失败,因为他们强制程序员将成功的情况和失败的情况分别写在两个不同的方法中并且有一个普通的继续(and then also have a common continuation)。例8、13和20分别展示了使用Choice, MultipleItemGather 和在一个迭代器中的Choice来进行显式错误处理(explicit error handling)。
  2. 隐式或者分布式一场处理被称作Causalities,它允许跨越多个异步操作嵌套和扩展。它与事务共享逻辑上下文概念或者将操作分组并为异步并发环境扩展它。这种机制可以跨越机器边界延伸。

因果关系(Causalities)

Causality是指可以跨多个执行上下文的一串操作,分支或者连接,创建一颗只有一个根的执行逻辑树。这个逻辑分组称为因果关系上下文,隐式的从一个消息的发送者传递到接收者。

Causalities是一种结构化异常机制在多线程环境的扩展。它们允许嵌套,但是依然处理多个并发的一场和与其他的causalities合并(例如因为连接(joins)操作)。

例22.

void SimpleCausalityExample()
{
    Port
<Exception> exceptionPort = new Port<Exception>();// create a causality using the port instance
    Causality exampleCausality = new Causality("root cause", exceptionPort);// add causality to current thread
    Dispatcher.AddCausality(exampleCausality);// any unhandled exception from this point on, in this method or
    
// any delegate that executes due to messages from this method,
    
// will be posted on exceptionPort.

    Port
<int> portInt = new Port<int>();
    Arbiter.Activate(_taskQueue,
        Arbiter.Receive(
false, portInt, IntHandler)
    );
// causalities flow when items are posted or Tasks are scheduled
    portInt.Post(0);// activate a handler on the exceptionPort
    
// This is the failure handler for the causality
    Arbiter.Activate(_taskQueue,
        Arbiter.Receive(
false, exceptionPort,
        
delegate(Exception ex)
        {
            
// deal with failure here
            Console.WriteLine(ex);
        })
    );
}
void IntHandler(int i)
{
    
// print active causalities
    foreach (Causality c in Dispatcher.ActiveCausalities)
    {
        Console.WriteLine(c.Name);
    }
// expect DivideByZeroException that CCR will redirect to the causality
    int k = 10 / i;
}

在上面的例子中我们演示了一个简单的场景,causalities帮助处理跨多个异步操作的错误。代码的关键步骤如下:

  1. 创建一个Port<Exception>实例exceptionPort来存储任何从causality上下文中抛出的异常。
  2. 使用上面创建的异常exceptionPort和一个友好的名称做为构造函数的参数来创建一个新的Causality实例。
  3. 将这个causality加入到Dispatcher的causalities列表中。
  4. 一个元素被投递到port,因为这个投递动作发生在causality的上下文中,他将会隐式的“附加”它的causality到被投递的元素。当一个处理器处理这个元素时,附加在元素上的causality将会被添加到执行接收器的线程中。任何接收器中抛出的异常将会被投递到causality中的exceptionPort。
  5. 一个接收器在exceptionPort上被激活,所以在所有异步操作中,所有的抛出但是没有被处理的异常都会被接收到。
  6. 因为有元素被投递到portInt实例上,所以处理器被调度,抛出一个DivideByZeroException。CCR调度器自动将这个异常重定向到关联在causality上的exceptionPort。

例23.

public void NestedCausalityExample()
{
    Port
<Exception> exceptionPort = new Port<Exception>();
    Causality parentCausality 
= new Causality(
        
"Parent",
        exceptionPort);
    Dispatcher.AddCausality(parentCausality);

    Port

<int> portInt = new Port<int>();
    Arbiter.Activate(_taskQueue,
        Arbiter.Receive(
false, portInt, IntHandlerNestingLevelZero)
    );
    portInt.Post(
0);// activate a handler on the exceptionPort
    
// This is the failure handler for the causality
    Arbiter.Activate(_taskQueue,
        Arbiter.Receive(
false,
        exceptionPort,
        
delegate(Exception ex)
        {
            
// deal with failure here
            Console.WriteLine("Parent:" + ex);
        })
    );
}
void IntHandlerNestingLevelZero(int i)
{
    
// print active causalities
    foreach (Causality c in Dispatcher.ActiveCausalities)
    {
        Console.WriteLine(
"Before child is added:   " + c.Name);
    }
// create new child causality that will nest under existing causality
    Port<Exception> exceptionPort = new Port<Exception>();
    Causality childCausality 
= new Causality(
        
"Child",
        exceptionPort);
    Dispatcher.AddCausality(childCausality);

    Arbiter.Activate(_taskQueue,
        Arbiter.Receive(

false,
        exceptionPort,
        
delegate(Exception ex)
        {
            
// deal with failure here
            Console.WriteLine("Child:" + ex);
        })
    );
// print active causalities
    foreach (Causality c in Dispatcher.ActiveCausalities)
    {
        Console.WriteLine(
"After child is added:    " + c.Name);
    }
// attach a receiver and post to a port
    Port<int> portInt = new Port<int>();
    Arbiter.Activate(_taskQueue,
        Arbiter.Receive(
false, portInt, IntHandlerNestingLevelOne)
    );
    portInt.Post(
0);
}
void IntHandlerNestingLevelOne(int i)
{
    
throw new InvalidOperationException("Testing causality support. Child causality will catch this one");
}

例子的输出:

Before child is added:   Parent
After child is added:    Child
Child:System.InvalidOperationException: Testing causality support. Child causality will catch this one
at Examples.Examples.IntHandlerNestingLevelOne(Int32 i)
 in C:\mri\main\CCR\testsrc\UnitTests\ccruserguideexamples.cs:line 571
at Microsoft.Ccr.Core.Task`1.Execute()
 in C:\mri\main\CCR\src\Core\Templates\GeneratedFiles\Task\Task01.cs:line 301
at Microsoft.Ccr.Core.TaskExecutionWorker.ExecuteTaskHelper(ITask currentTask)
 in C:\mri\main\CCR\src\Core\scheduler_roundrobin.cs:line 1476
at Microsoft.Ccr.Core.TaskExecutionWorker.ExecuteTask(ITask& currentTask, DispatcherQueue p)
 in C:\mri\main\CCR\src\Core\scheduler_roundrobin.cs:line 1376
at Microsoft.Ccr.Core.TaskExecutionWorker.ExecutionLoop()
 in C:\mri\main\CCR\src\Core\scheduler_roundrobin.cs:line 1307
 

在上面的例子中,我们演示了一个嵌套的异步序列:

  1. NestedCausalityExample方法向一个带接收器的port中加入了一个causality
  2. IntHandlerNestingLevelZero方法与NestedCausalityExample方法并行异步执行,向

连接和因果关系(Joins and Causalities)

CCR的因果关系实现了一个独一无二的功能:它可以组合从两个不同的执行路径来的因果关系。类似于结构化异常,但是推广到并发的情况。因果关系可以嵌套,给程序员在内部外部因果关系中处理异常或者显式的投递到父因果关系中。

例24.

public void JoinedCausalityExample()
{
    Port
<int> intPort = new Port<int>();
    Port
<int> leftPort = new Port<int>();
    Port
<string> rightPort = new Port<string>();

    Port

<Exception> leftExceptionPort = new Port<Exception>();
    Port
<Exception> rightExceptionPort = new Port<Exception>();// post twice so two handlers run
    intPort.Post(0);
    intPort.Post(
1);// activate two handlers that will execute concurrently and create
    
// two different parallel causalities
    Arbiter.Activate(_taskQueue,
        Arbiter.Receive(
false, intPort,
        
delegate(int i)
        {

            Causality leftCausality 

= new Causality("left", leftExceptionPort);
            Dispatcher.AddCausality(leftCausality);
            
// post item on leftPort under the context of the left causality
            leftPort.Post(i);
        })
    );

    Arbiter.Activate(_taskQueue,
        Arbiter.Receive(

false, intPort,
        
delegate(int i)
        {
            Causality rightCausality 
= new Causality("right", rightExceptionPort);
            Dispatcher.AddCausality(rightCausality);
            
// post item on rightPort under the context of the right causality
            rightPort.Post(i.ToString());
        })
    );
// activate one join receiver that executes when items are available on
    
// both leftPort and rightPort

    Arbiter.Activate(_taskQueue,
        Arbiter.JoinedReceive
<intstring>(false, leftPort, rightPort,
        
delegate(int i, string s)
        {
            
throw new InvalidOperationException("This exception will propagate to two peer causalities");
        })
    );
// activate a handler on the exceptionPort
    
// This is the failure handler for the causality
    Arbiter.Activate(_taskQueue,
        Arbiter.Receive(
false, leftExceptionPort,
        
delegate(Exception ex)
        {
            
// deal with failure here
            Console.WriteLine("Left causality:  " + ex);
        })
    );
// activate a handler on the exceptionPort
    
// This is the failure handler for the causality
    Arbiter.Activate(_taskQueue,
        Arbiter.Receive(
false, rightExceptionPort,
        
delegate(Exception ex)
        {
            
// deal with failure here
            Console.WriteLine("Right causality:  " + ex);
        })
    );
}

例子的输出:

Left causality:  System.InvalidOperationException: This exception will propagate to two peer causalities
Right causality:  System.InvalidOperationException: This exception will propagate to two peer causalities
 

为了可读性,上面的例子中大量使用了匿名方法来保持所有的逻辑都在一个方法中。这个例子演示了:

  • 两个独立的处理器创建了两个causalities(分别叫做:"left"和"right")
  • 向每个port中投递一个消息
  • 由于在两个port(leftPort和rightPort)上的连接条件被满足,所以第三个处理器被执行
  • 连接处理器在两个causalities的上下文中抛出一个异常

关键点是:当连接处理器(join handler)执行并且抛出异常,两个peer causalities被激活并且都独立的得到了投递到他们所负责的exception port上的异常。

重要:由于causalities使用常规的CCR ports来接收异常,你可以使用CCR协调原语来组合跨多个causalitites的异常处理。Joins,interleave和choice全都是合适的而且强大的方式来组合跨多个并发,多层异步操作的异常处理。