使用 Task.Delay() 进行循环会造成内存泄漏吗?

问题描述 投票:0回答:3

我正在实现一个异步缓冲系统,我希望队列的一个消费者能够保证项目按顺序处理。消费者应该定期检查队列,处理其中的所有项目,然后“睡眠”一段时间。 Task.Delay() 似乎非常适合这样的系统,因为与 Thread.Sleep() 不同,它在睡眠时不会消耗线程,并且与 Timer 不同,如果处理队列项花费的时间超过睡眠间隔,它不会启动新线程。但是,我想知道如果任务系统正在跟踪原始任务的整个延续列表,在 while 循环中使用 Task.Delay() 是否会造成内存泄漏。作为参考,我的系统如下所示:

void EnqueueItem(Item item) {
    lock (this._lock) { this._items.Add(item); }
}

async Task Consumer() {
    while (true) {
        await Task.Delay(interval).ConfigureAwait(false);

        Item[] items = null;
        lock (this._lock) {
            if (this._disposed) { return; }
            if (this._items.Count > 0)
            {
                items = this._items.ToArray();
                this._items.Clear();
            }
        }
        if (items != null) { Process(items); }
    }
}

// in the constructor of the buffer
this.Consumer();
c# task async-await
3个回答
6
投票

我正在实现一个异步缓冲系统...

我强烈建议您使用现有的。 TPL Dataflow 是我的首要推荐。但如果您的平台上不可用,那么 my

AsyncProducerConsumerQueue
是一个选项。

此设置允许您的消费者仅使用

ReceiveAsync
/
DequeueAsync
,而无需
Task.Delay

也就是说,我不相信会出现像你所描述的那样的内存泄漏。不过,我实际上还没有在分析器中运行它来验证这一点。


3
投票

它不会导致内存泄漏,但是如果您处于紧密循环中,您可能想要处理任务而不是等待它们的终结器

例如

var t = Task.Delay(interval);
await t.ConfigureAwait(false);
t.Dispose();

但您可能不想,也不应该需要(请参阅我需要处置任务吗?


0
投票

是的,它可能会泄漏内存。

[Test]
public async Task TaskDelay()
{
    
    for (int i = 0; i < int.MaxValue; i++)
    {
        var t = Task.Delay(i);
        await Task.WhenAny(t, NoOp());
    }
}
public async Task NoOp()
{
    await Task.Yield();
}

会泄漏内存。

取消令牌需要传递给

Task.Delay
,并且需要取消它以阻止泄漏,例如这个不会漏的

public async Task TaskDelay()
{
    
    for (int i = 0; i < int.MaxValue; i++)
    {
        using var cts = new CancellationTokenSource();
        var t = Task.Delay(i, cts.Token);
        await Task.WhenAny(t, NoOp());
        cts.Cancel();
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.