相关疑难解决方法(0)

异步/等待作为协同程序的替代品

我使用C#迭代器作为协同程序的替代品,它一直很好用.我想切换到async/await,因为我认为语法更清晰,它给了我类型安全. 在这篇(过时的)博客文章中,Jon Skeet展示了实现它的可能方式.

我选择采用略有不同的方式(通过实现我自己SynchronizationContext和使用Task.Yield).这很好.

然后我意识到会有问题; 目前协程不必完成运行.它可以在任何产生的点上优雅地停止.我们可能有这样的代码:

private IEnumerator Sleep(int milliseconds)
{
    Stopwatch timer = Stopwatch.StartNew();
    do
    {
        yield return null;
    }
    while (timer.ElapsedMilliseconds < milliseconds);
}

private IEnumerator CoroutineMain()
{
    try
    {
        // Do something that runs over several frames
        yield return Coroutine.Sleep(5000);
    }
    finally
    {
        Log("Coroutine finished, either after 5 seconds, or because it was stopped");
    }
}
Run Code Online (Sandbox Code Playgroud)

协程通过跟踪堆栈中的所有枚举器来工作.C#编译器生成一个Dispose函数,可以调用该函数以确保正确调用'finally'块CoroutineMain,即使枚举未完成.这样我们就可以优雅地停止协程,并通过调用堆栈Dispose上的所有IEnumerator对象来确保最终调用块.这基本上是手动展开.

当我用async/await编写我的实现时,我意识到我们会失去这个功能,除非我弄错了.然后我查找了其他协同解决方案,看起来Jon Skeet的版本看起来也没有任何处理方式.

我能想到处理这个问题的唯一方法就是拥有我们自己的自定义'Yield'函数,它会检查协程是否被停止,然后引发一个表明这个的异常.这将传播,执行finally块,然后被捕获到根附近的某处.我不觉得这很漂亮,因为第三方代码可能会捕获异常.

我误解了什么,这是否可以更容易地做到?或者我需要以异常方式执行此操作吗?

编辑:已请求更多信息/代码,所以这里有一些.我可以保证这只会在一个线程上运行,所以这里没有涉及线程.我们当前的协程实现看起来有点像这样(这是简化的,但它适用于这个简单的情况):

public sealed …
Run Code Online (Sandbox Code Playgroud)

c# asynchronous coroutine async-await

14
推荐指数
2
解决办法
7022
查看次数

IAsyncEnumerable,从事件处理程序产生

我正在尝试使用基于IAsyncEnumerable. 基本上沿着以下路线:

async IAsyncEnumerable<string> ReadAll() 
{
    var reader = new EventBasedReader();
    reader.OnRead => (_, args) => yield return e.Message;
    reader.Start();
    await reader.WaitUntilAllRead();
}
Run Code Online (Sandbox Code Playgroud)

但是,这不起作用,因为它是产生的事件处理程序,这是不允许的。有没有另一种方法可以编写它以使其作为IAsyncEnumerable.

c# async-await

4
推荐指数
1
解决办法
315
查看次数

标签 统计

async-await ×2

c# ×2

asynchronous ×1

coroutine ×1