尾递归与任务? [英] Tail recursion with tasks?

查看:68
本文介绍了尾递归与任务?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在看这篇有关尾巴递归的文章在C#中

我得到了他们使用的一般示例(Factorial),并且我试图将其应用于使用Task递归的情况.我在尝试使用没有返回类型的Task来应用上面的示例时遇到问题.

I get the general example that they used (Factorial) and I was trying to apply it to a situation in which I use Task recursion. I am having a problem trying to apply the example above with a Task that has no return type.

想象一下我在C#中具有以下类和方法:

Imagine I have the following class and methods in C#:

public class Crawler(CancellationToken token)
{
    this.cancelToken = token;
}

public async Task CrawlData(string dataLocation, bool repeat = false)
{
    /*Most of processing done here, omitted for space...*/
    while(canCrawl == false)
    {
        await Task.Delay(250);
    }
    await CrawlData(dataLocation, repeat);
}

当前效果很好!由于canCrawl等于false的延迟,它可以运行很长时间(几天,几周,几个月),没有问题.

Currently this works great! It can run for a very long time (days, weeks, months) without issue because of a delay which is when canCrawl is equal to false.

如果有人感到困惑,我在Tasks中使用递归的原因是因为我从另一个依赖Task.Run()的类中调用了此方法以最初执行此方法,并且它依赖于Task一直存在递归仍在进行,以使该主类中的执行不会在第一次调用后跳到结尾.所有这些都与在执行过程中能够取消任务有关.

If anyone is confused, the reason I am using recursion with Tasks is because I call this method from another class which relies on Task.Run() to initially execute this method, and it relies on that Task being alive as long as the recursion is still going so that execution in that main class does not jump to the end after the first call. This all has to do with being able to cancel the task while in progress.

我担心的是,如果此命令运行 足够长的时间 ,最终将导致StackOverflowException,就像Factorial示例在链接中所做的那样帖子的开头.

My concern is that if this were to run for long enough, it would eventually lead to a StackOverflowException just as the Factorial example did in the link at the beginning of the post.

我尝试将该示例应用于没有返回类型的Task,这是我正在使用的,但是我没有任何运气.

I've tried applying that example to a Task with no return type which is what I am using, but I am not having any luck.

您有没有使用Task在C#中进行尾递归的经验?

Do any of you have experience with tail recursion in C# with Task?

推荐答案

如果您要询问是否会得到StackOverflowException,答案是确定否.

If you're asking whether you'll get a StackOverflowException the answer is a definite no.

由于该方法是异步方法,因此在await之后恢复时不会保留堆栈.因此,经过250ms的延迟后,保持运行状态的线程将拥有新的干净堆栈.

Because the method is an async one, it doesn't preserve the stack when resuming after an await. So after a delay of 250ms the thread that keeps running will have a new and clean stack.

但是,您最终将得到一个OutOfMemoryException,因为每个异步方法都转换为状态机结构,该状态机被装箱并保持活动状态,直到该方法完成为止(在您的情况下,这毕竟是它的尾部调用已完成).

However, you will eventually get an OutOfMemoryException because each async method is transformed into a state machine struct which is boxed and kept alive until that method completes (which in your case is only after all its tail calls complete).

Task.Delay太慢,无法模拟这种行为,但是您可以使用Task.Yield做到这一点,它异步完成(如Task.Delay)但立即完成(与Task.Delay至少延迟15ms)不同.此示例在不到一分钟的时间内用完了我机器上的内存.

Task.Delay is too slow to simulate this behavior but you can do that with Task.Yield which completes asynchronously (like Task.Delay) but immediately (unlike Task.Delay which has a delay of at least 15ms). This example runs out of memory on my machine in less than a minute.

private static void Main()
{
    FooAsync().Wait();
}

private static async Task FooAsync()
{
    await Task.Yield();
    await FooAsync();
}

如果您设法拥有一个不是异步的任务返回方法,但是使用延续来完成您需要做的事情,那么它可以避免该异常,但这取决于实现方式.

If you manage to have a task-returning method that isn't async but does what you need using continuations than it may avoid that exception, but that depends on the implementation.

如果有人感到困惑,我对Tasks使用递归的原因是因为我从另一个依赖Task.Run()的类中调用了此方法,因此最初会执行此方法"

"If anyone is confused, the reason I am using recursion with Tasks is because I call this method from another class which relies on Task.Run() to initially execute this method"

在这种情况下,可能没有理由使用Task.Run.您可以简单地调用该方法并获得一个任务作为返回值.

There's probably no reason to use Task.Run in this case. You can simply call the method and get a task as return-value.

这篇关于尾递归与任务?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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