Task.Delay引发内存泄露

在一次Demo中用到Task.Delay导致内存泄露

例子代码如下:

class Program
    {
        static void Main(string[] args)
        {
            TestAbc que = new TestAbc();           
            Task.Run(() =>
            {
                while (true)
                {

                    var logs = que.Dequeue();
                    {
                        logs?.ToList().ForEach(x =>
                        {
                            if (x != null)
                                Console.WriteLine($"{(int)x.Text.LogLevel}{x.Text.Pos:000000}");
                        });
                    }
                    Task.Delay(100);
                }
            });
            for (int i = 0; i < 10; i++)
            {
                var x = i;
                Task.Run(() =>
                {
                    for (int k = 0; k < 100; k++)
                    {                       
                        var rnd = new Random();
                        for (int j = 0; j < 30000; j++)
                        {
                            que.Enqueue(new LoggerInfo
                            {
                                Text = new LoggerInfoClass
                                {
                                    Pos = j,
                                    LogLevel = (LogLevel)x,
                                    Text = $"{Thread.CurrentThread.ManagedThreadId}-{rnd.Next(9999999).ToString()}-{rnd.Next(9999999).ToString()}"
                                }
                            });
                        }

                    }
                });              
            }
            Thread.Sleep(3000000);
        }

    }
  

区区11个Task就导致内存疯狂飙升,2G,3G,4G…
把下面的11个task修改为Thread,一切OK,难道说微软开发的 Task有内存泄露问题?ooo,不应该是这样的啊。
再次review代码,发现有一个地方可疑。你注意到了吗??

Task.Delay()

Thread.Sleep()是同步延迟,Task.Delay()是异步延迟。
Thread.Sleep()会阻塞线程,Task.Delay()不会。
Thread.Sleep()不能取消,Task.Delay()可以。
Task.Delay()实质创建一个运行给定时间的任务,Thread.Sleep()使当前线程休眠给定时间。
反编译Task.Delay(),基本上讲它就是个包裹在任务中的定时器。
Task.Delay()和Thread.Sleep()最大的区别是Task.Delay()旨在异步运行,在同步代码中使用Task.Delay()是没有意义的;在异步代码中使用Thread.Sleep()是一个非常糟糕的主意。通常使用await关键字调用Task.Delay()。

难道是我没有等待 Task.Delay()的原因?我修改为等待试试。
Task.Delay(100).Wait();
再次编译尝试,好了,内存并没有再泄露。
再次看看对Task.Delay()的描述,原来Task.Delay在幕后使用了一个计时器线程,如果没有等待的话,后果很严重,就是线程池可能会耗尽线程,并且内存中大部分泄露也是计数器引起的。不过非常奇怪的是,如果没有下面的task数组,则并没有导致内存泄露。
看起来还是有些像bug。

开大最先线程池数

因为可能是池子太小导致的内存泄露bug,那我们开大线程池看啊看。

ThreadPool.SetMinThreads(300, 300);
ThreadPool.SetMaxThreads(300, 300);

嗯嗯,再次尝试运行,果然并没有内存泄露。oh,my god!
同步中慎用 非等待的异步Task,否则,你就瞧好吧!!!

发布了112 篇原创文章 · 获赞 16 · 访问量 25万+

猜你喜欢

转载自blog.csdn.net/webmote/article/details/101699715
今日推荐