gpt4 book ai didi

c# - (错误)使用 C# 迭代器实现协程的陷阱

转载 作者:可可西里 更新时间:2023-11-01 07:46:01 26 4
gpt4 key购买 nike

我正在编写重构 Silverlight 程序以使用 WCF 服务中的部分现有业务逻辑。这样做时,我遇到了 Silverlight 3 中的限制,它只允许对 WCF 服务进行异步调用,以避免长时间运行或无响应的服务调用阻塞 UI 线程的情况(SL 有一个有趣的队列模型来调用 WCF 服务在 UI 线程上)。

因此,编写曾经简单明了的内容正变得越来越复杂(请参阅问题末尾的代码示例)。

理想情况下,我会使用 coroutines为了简化实现,但遗憾的是,C# 目前不支持协程作为本地语言工具。但是,C# 确实有使用 yield return 的生成器(迭代器)的概念。句法。我的想法是重新调整 yield 关键字的用途,以允许我为相同的逻辑构建一个简单的协程模型。

但是,我不愿意这样做,因为我担心可能会有一些我没有预料到的隐藏(技术)陷阱(考虑到我对 Silverlight 和 WCF 的相对缺乏经验)。我还担心 future 的开发人员可能不清楚实现机制,并且可能会阻碍而不是简化他们 future 维护或扩展代码的工作。我在 SO 上看到了这个关于重新利用迭代器来构建状态机的问题:implementing a state machine using the "yield" keyword ,虽然这与我正在做的事情并不完全相同,但它确实让我停下来。

但是,我需要做一些事情来隐藏服务调用的复杂性,并管理此类更改中的工作量和潜在的缺陷风险。我对可以用来解决这个问题的其他想法或方法持开放态度。

代码的原始非 WCF 版本如下所示:

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 版本变得更加复杂(即使没有异常处理和前后条件测试):

// 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();
}

上面的代码显然是一种简化,因为它省略了异常处理、无效性检查和生产代码中必需的其他做法。尽管如此,我认为它证明了 Silverlight 中的异步 WCF 编程模型开始出现复杂性的快速增加。重构原始实现(它没有使用服务层,而是将其逻辑嵌入到 SL 客户端中)很快就会成为一项艰巨的任务。并且很可能很容易出错。

代码的协程版本看起来像这样(我还没有测试过):

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();
}

上面的代码有各种各样需要改进的地方。但基本前提是分解出延续模式(为异常处理和各种检查创建拦截点),同时允许 WCF 基于事件的异步模型在执行每个步骤时驱动 - 基本上是在最后一个异步 WCF 调用完成时。虽然从表面上看这看起来像是更多代码,但值得一提的是 PerformSteps()NextStep()是可重用的,只有在ButtonClickCoRoutine()中实现会随着每个不同的实现站点而变化。

我不确定我是否喜欢这个模型,如果有更简单的方法来实现它,我也不会感到惊讶。但是我无法在“interwebs”或 MSDN 或其他任何地方找到一个。预先感谢您的帮助。

最佳答案

你绝对应该看看 Concurrency and Coordination Runtime .它使用迭代器正是为了这个目的。

另一方面,你也应该看看Parallel Extensions及其延续的方法。 Parallel Extensions 是 .NET 4.0 的一部分,而 CCR 需要单独的许可。不过,我建议您使用由吃、呼吸和 sleep 这些东西的人编写的框架。自己很容易弄错细节。

关于c# - (错误)使用 C# 迭代器实现协程的陷阱,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1903531/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com