(错误)的缺陷使用C#迭代器来实现协同程序 [英] Pitfalls of (Mis)Using C# Iterators to Implement Coroutines

查看:127
本文介绍了(错误)的缺陷使用C#迭代器来实现协同程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我写重构一个Silverlight程序从一个WCF服务使用其现有的业务逻辑的一部分。这样做,我碰到在Silverlight 3的限制,只允许异步调用WCF到服务,以避免在那里长时间运行或无响应服务呼叫阻塞UI线程的情况下(SL拥有用于调用WCF服务的一个有趣的排队模型在UI线程)。

I am writing refactoring a Silverlight program to consumes a portion of its existing business logic from a WCF service. In doing so, I've run into the restriction in Silverlight 3 that only allows asynchronous calls to WCF services to avoid cases where long-running or non-responsive service calls block the UI thread (SL has an interesting queuing model for invoking WCF services on the UI thread).

因此​​,写作曾经是简单的,正成为快速更复杂(的见code的例子在我的问题的最后的)。

As a consequence, writing what once was straightforward, is becoming rapidly more complex (see the code examples at the end of my question).

在理想情况下,我会用协程简化实施,但可悲的是,C#目前不支持协同程序作为本机语言能力。然而,C#确实有发电机(迭代器)概念使用收益回报率语法。我的想法是重新利用yield关键字,让我建立同样的逻辑简单的协同程序的模型。

Ideally, I would use coroutines to simplify the implementation, but sadly, C# does not currently support coroutines as a native language facility. However, C# does have the concept of generators (iterators) using the yield return syntax. My idea is to re-purpose the yield keyword to allow me to build a simple coroutine model for the same logic.

我不愿意这样做,但是,因为我担心,有可能是我没有预料(给我的与Silverlight和WCF相对缺乏经验)一些隐藏的(技术)的陷阱。我也担心,实施机制可能不是很清楚未来的发展,并可能阻碍而不是简化自己的努力来保持或在将来扩展code。我已经看到了这个问题的SO关于再重新考虑迭代器来构建状态机:<一href=\"http://stackoverflow.com/questions/1194853/implementing-a-state-machine-using-the-yield-keyword\">http://stackoverflow.com/questions/1194853/implementing-a-state-machine-using-the-yield-keyword,虽然它不正是我在做同样的事情,这确实让我暂停。

I am reluctant to do this, however, because I am worried that there may be some hidden (technical) pitfalls that I'm not anticipating (given my relative inexperience with Silverlight and WCF). I am also worried that the implementation mechanism may not be clear to future developers and may hinder rather than simplify their efforts to maintain or extend the code in the future. I've seen this question on SO about re-purposing iterators to build state machines: http://stackoverflow.com/questions/1194853/implementing-a-state-machine-using-the-yield-keyword, and while it's not exactly the same thing I'm doing, it does make me pause.

不过,我需要做一些隐藏的服务调用的复杂性和管理这种类型变化的努力和缺陷的潜在风险。我打开其他的想法或做法,我可以用它来解决这个问题。

However, I need to do something to hide the complexity of the service calls and manage the effort and potential risk of defects in this type of change. I am open to other ideas or approaches I can use to solve this problem.

在code的原非WCF的版本看起来是这样的:

The original non-WCF version of the code looks something like this:

void Button_Clicked( object sender, EventArgs e ) {
   using( var bizLogic = new BusinessLogicLayer() ) {
       try  {
           var resultFoo = bizLogic.Foo();
           // ... do something with resultFoo and the UI
           var resultBar = bizLogic.Bar(resultFoo);
           // ... do something with resultBar and the UI
           var resultBaz = bizLogic.Baz(resultBar);
           // ... do something with resultFoo, resultBar, resultBaz
       }
   }
}

重新分解WCF版本变得很更复杂(即使没有异常处理和pre /后置条件测试)了一下:

The re-factored WCF version becomes quite a bit more involved (even without exception handling and pre/post condition testing):

// fields needed to manage distributed/async state
private FooResponse m_ResultFoo;  
private BarResponse m_ResultBar;
private BazResponse m_ResultBaz;
private SomeServiceClient m_Service;

void Button_Clicked( object sender, EventArgs e ) {
    this.IsEnabled = false; // disable the UI while processing async WECF call chain
    m_Service = new SomeServiceClient();
    m_Service.FooCompleted += OnFooCompleted;
    m_Service.BeginFoo();
}

// called asynchronously by SL when service responds
void OnFooCompleted( FooResponse fr ) {
    m_ResultFoo = fr.Response;
    // do some UI processing with resultFoo
    m_Service.BarCompleted += OnBarCompleted;
    m_Service.BeginBar();
}

void OnBarCompleted( BarResponse br ) {
    m_ResultBar = br.Response;
    // do some processing with resultBar
    m_Service.BazCompleted += OnBazCompleted;
    m_Service.BeginBaz();
} 

void OnBazCompleted( BazResponse bz ) {
    m_ResultBaz = bz.Response;
    // ... do some processing with Foo/Bar/Baz results
    m_Service.Dispose();
}

以上code显然是一个简化的,因为它忽略了异常处理,无效检查和其他做法,将在生产code必要的。尽管如此,我认为这表明了复杂性大大提高,开始在Silverlight异步WCF编程模型发生。再分解的最初的实现(没有使用服务层,而是已经其嵌入在对SL客户端逻辑)正迅速寻找是一个艰巨的任务。和一个很可能是相当容易出错。

The above code is obviously a simplification, in that it omits exception handling, nullity checks, and other practices that would be necessary in production code. Nonetheless, I think it demonstrates the rapid increase in complexity that begins to occur with the asynchronous WCF programming model in Silverlight. Re-factoring the original implementation (which didn't use a service layer, but rather had its logic embedded in the SL client) is rapidly looking to be a daunting task. And one that is likely to be quite error prone.

在code的协同例程的版本将是这个样子(我没有测试过这还):

The co-routine version of the code would look something like this (I have not tested this yet):

void Button_Clicked( object sender, EventArgs e ) {
    PerformSteps( ButtonClickCoRoutine );
}

private IEnumerable<Action> ButtonClickCoRoutine() {
    using( var service = new SomeServiceClient() ) {
        FooResponse resultFoo;
        BarResponse resultBar;
        BazResponse resultBaz;

        yield return () => {
            service.FooCompleted = r => NextStep( r, out resultFoo );
            service.BeginFoo();
        };
        yield return () => {
            // do some UI stuff with resultFoo
            service.BarCompleted = r => NextStep( r, out resultBar );
            service.BeginBar();
        };
        yield return () => {
            // do some UI stuff with resultBar
            service.BazCompleted = r => NextStep( r, out resultBaz );
            service.BeginBaz();
        };
        yield return () => {
            // do some processing with resultFoo, resultBar, resultBaz
        }
    }
}

private void NextStep<T>( T result, out T store ) { 
    store = result;
    PerformSteps();  // continues iterating steps
}

private IEnumerable<Action> m_StepsToPerform;
private void PerformSteps( IEnumerable<Action> steps ) {
   m_StepsToPerform = steps;
   PerformSteps();        
}

private void PerformSteps() {
   if( m_StepsToPerform == null ) 
       return; // nothing to do

   m_StepsToPerform.MoveNext();
   var nextStep = m_StepsToPerform.Current;
   if( nextStep == null ) {
       m_StepsToPerform.Dispose();
       m_StepsToPerform = null;
       return; // end of steps
   }
   nextStep();
}

有需要在上述code有待提高各种东西。但基本premise是分解出的延续图案(创建异常处理和各种检查拦截点),同时允许WCF的基于事件的异步模式,当执行每个步骤驱动 - 基本上当最后异步WCF调用完成。虽然从表面上看,这看起来像更多code上,值得一提的是, PerformSteps() NextStep自动()可重复使用,只有在 ButtonClickCoRoutine实现()将与每个不同的实现网站更改。

There are all sorts of things that need to be improved in the above code. But the basic premise is to factor out the continuation pattern (creating an interception point for exception handling and various checks) while allowing the event-based async model of WCF to drive when each step is performed - basically when the last async WCF call completes. While on the surface this looks like more code, it's worth mentioning that PerformSteps() and NextStep() are reusable, only the implementation in ButtonClickCoRoutine() would change with each different implementation site.

我不完全知道我喜欢这种模式,如果一个简单的方式存在,以实现它,我也不会感到惊讶。但我一直没能找到一个关于interwebs或MSDN,或任何其他地方。在此先感谢您的帮助。

I'm not entirely sure I like this model, and I wouldn't be surprised if a simpler way existed to implement it. But I haven't been able to find one on the "interwebs" or MSDN, or anywhere else. Thanks in advance for the help.

推荐答案

您绝对应该看协调和并发运行时的。它采用迭代出于这样的目的。

You should definitely look at the Coordination and Concurrency Runtime. It uses iterators for exactly this purpose.

在另一方面,你也应该看看并行扩展以及其延续的方法。并行扩展是.NET 4.0的一部分,而CCR需要单独的许可。我的的建议你去与谁吃饭,呼吸,睡觉,虽然这东西的人写的框架。这只是太容易错得对自己的详细信息。

On the other hand, you should also look at Parallel Extensions and its approach to continuations. Parallel Extensions is part of .NET 4.0, whereas the CCR requires separate licensing. I would advise you to go with a framework written by people who eat, breathe and sleep this stuff though. It's just too easy to get details wrong on your own.

这篇关于(错误)的缺陷使用C#迭代器来实现协同程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆