昨天在博客园有园友问了我一个问题,是这样的:

先是半个月前 @碧水青荷 童鞋的一句话“大家都说不要随便 Task.Run(()=>{}) 这样写”,当时没有想太多,这句话并没有引起我注意,只顾着回答他“不想在代码中加 async/await 该怎么做”的问题。

然后这句话被 @裤兜 童鞋注意到,昨天问了我为什么。我当时也很纳闷,Task.Run并行场景中很常见啊,为什么大家会有不要随便使用的说法。很遗憾,我当时脑海里认为这种说法只是空穴来风,并没有细究。

我有个习惯,就是下班路上在地铁上快速复盘一下今天发生的事情。当时这个问题刚好就在脑海里闪现了一下,“为什么大家都说不要随便使用 Task.Run”。突然想起了多年前的一个晚上……哦,难道是“Ta”?

对,应该就是它,内存泄露,除了这个原因我再也想不到其它原因了。因为我隐约记得多年前我确实踩过一次这个坑,也可能是两次。

没错,Task.Run 使用不当,一不留意就会有内存泄露的问题。

我们先来看一段代码:

public class MyClass
{
private int _id;
private Logger<MyClass> _logger; public MyClass(Logger<MyClass> logger)
{
_logger = logger;
} public Task Foo(Logger<MyClass> logger)
{
return Task.Run(() =>
{
_logger.LogInformation($"Executing job with ID {_id}");
// do sth.
});
}
}

在这段代码中,私有成员 _idTask.Run 的匿名方法捕获使用,进而导致 MyClass 实例被引用。当外部使用完 MyClass 实例时,本该由 GC 回收的时候却发现它还被其它资源引用着,所以 GC 认为该实例不应用被回收,也就永远失去了被回收的机会。

道理很简单,我就不再用示例演示了。解决办法也很简单,想必很多人都知道,就是使用本地变量

public class MyClass
{
private int _id;
private Logger<MyClass> _logger; public MyClass(Logger<MyClass> logger)
{
_logger = logger;
} public Task Foo(Logger<MyClass> logger)
{
var localId = _id;
return Task.Run(() =>
{
_logger.LogInformation($"Executing job with ID {localId}");
// do sth.
});
}
}

通过将值分配给一个本地变量,类就没有成员被捕获,即避免了潜在的内存泄漏。

内存泄漏问题在 Task.Run 身上发生很常见,容易被大家记住,容易提高警觉。其实不光是 Task.Run,其它地方使用了匿名方法也同样要小心,比如这个示例:

public class MyClass
{
private int _id;
private Logger<MyClass> _logger;
private JobQueue _jobQueue; public MyClass(Logger<MyClass> logger, JobQueue jobQueue)
{
_logger = logger;
_jobQueue = jobQueue;
} public void Foo()
{
_jobQueue.EnqueueJob(() =>
{
_logger.LogInformation($"Executing job with ID {_id}");
// do sth.
});
}
}

也有内存泄漏的问题。

总之,任何使用匿名方法的地方都要避免捕获类的成员,小心内存泄漏

为什么要小心使用 Task.Run的更多相关文章

  1. 小心使用 Task.Run 续篇

    关于前两天发布的文章:为什么要小心使用 Task.Run,对文中演示的示例到底会不会导致内存泄露,给很多人带来了疑惑.这点我必须向大家道歉,是我对导致内存泄漏的原因没描述和解释清楚,也没用实际的示例证 ...

  2. 小心使用 Task.Run 解惑篇

    继上一篇文章之后,这篇文章主要解答以下两个疑惑: 由于值类型是拷贝的方式赋值,所以捕获的本地变量和类成员是指向的是各自的值,对本地变量的捕获不会影响到整个类.但如果把 _id 改为引用类型(如 Str ...

  3. 对 精致码农大佬 说的 Task.Run 会存在 内存泄漏 的思考

    一:背景 1. 讲故事 这段时间项目延期,加班比较厉害,博客就稍微停了停,不过还是得持续的技术输出呀! 园子里最近挺热闹的,精致码农大佬分享了三篇文章: 为什么要小心使用 Task.Run [http ...

  4. (转).NET 4.5中使用Task.Run和Parallel.For()实现的C# Winform多线程任务及跨线程更新UI控件综合实例

    http://2sharings.com/2014/net-4-5-task-run-parallel-for-winform-cross-multiple-threads-update-ui-dem ...

  5. Task.Run Vs Task.Factory.StartNew

    在.Net 4中,Task.Factory.StartNew是启动一个新Task的首选方法.它有很多重载方法,使它在具体使用当中可以非常灵活,通过设置可选参数,可以传递任意状态,取消任务继续执行,甚至 ...

  6. Task.Run Vs Task.Factory.StartNew z

    在.Net 4中,Task.Factory.StartNew是启动一个新Task的首选方法.它有很多重载方法,使它在具体使用当中可以非常灵活,通过设置可选参数,可以传递任意状态,取消任务继续执行,甚至 ...

  7. .Net4.0如何实现.NET4.5中的Task.Run及Task.Delay方法

    前言 .NET4.0下是没有Task.Run及Task.Delay方法的,而.NET4.5已经实现,对于还在使用.NET4.0的同学来说,如何在.NET4.0下实现这两个方法呢? 在.NET4.0下, ...

  8. Task.Run与Task.Factory.StartNew的区别

    Task是可能有延迟的工作单元,目的是生成一个结果值,或产生想要的效果.任务和线程的区别是:任务代表需要执行的作业,而线程代表做这个作业的工作者. 在.Net 4中,Task.Factory.Star ...

  9. C# Task.Run 和 Task.Factory.StartNew 区别

    Task.Run 是在 dotnet framework 4.5 之后才可以使用,但是 Task.Factory.StartNew 可以使用比 Task.Run 更多的参数,可以做到更多的定制.可以认 ...

随机推荐

  1. 11张图和源码带你解析Spring Bean的生命周期,建议收藏~!

    在网上已经有跟多Bean的生命周期的博客,但是很多都是基于比较老的版本了,最近把整个流程画成了一个流程图.待会儿使用流程图,说明以及代码的形式来说明整个声明周期的流程.注意因为代码比较多,这里的流程图 ...

  2. C# 中的 ref 已经被放开,或许你已经不认识了

    一:背景 1. 讲故事 最近在翻 netcore 源码看,发现框架中有不少的代码都被 ref 给修饰了,我去,这还是我认识的 ref 吗?就拿 Span 来说,代码如下: public readonl ...

  3. 做Java开发7年,今年9个月时间圆梦饿了么P7

    前言 工作拧螺丝,面试造火箭.我想这是每个程序员比较头疼的事情吧!但是,又必须要经历一个面试流程,尤其是摸不清面试官问的问题,导致面试的时候不知道如何回答.本文是工作7年Java程序员从几十次面试中挑 ...

  4. 知识管理——得到CEO脱不花女士的一次分享

    知识管理--得到CEO脱不花女士的一次分享 近日,公司举办了一场"CKO首席知识官"研讨会,邀请到了得到APP的CEO脱不花女士做了一场精彩的分享,让我深受启发. 分享内容围绕3个 ...

  5. C语言实现天气信息管理系统

    这篇文章主要介绍了C语言实现天气信息管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 本文实例为大家分享了C语言实现天气信息管理系统的具体代码,供大家参考,具体内 ...

  6. Netty源码解析 -- 零拷贝机制与ByteBuf

    本文来分享Netty中的零拷贝机制以及内存缓冲区ByteBuf的实现. 源码分析基于Netty 4.1.52 Netty中的零拷贝 Netty中零拷贝机制主要有以下几种 1.文件传输类DefaultF ...

  7. Redis系列文章-数据结构篇

    Redis系列文章 前言: 工作原因,在学习mybatis知识后,2个月没有补充新的知识了,最近拿起书本开始学习.打算写下这个Redis系列的文章. 目录结构如下: Redis内置数据结构 Redis ...

  8. 经典c程序100例==51--60

    [程序51] 题目:学习使用按位与 & . 1.程序分析:0&0=0; 0&1=0; 1&0=0; 1&1=1 2.程序源代码: #include " ...

  9. JS多物体宽度运动案例

    任务 对于每一个Div区块,鼠标移入,宽度逐渐变宽,最宽值为400px,当鼠标移除时,宽度逐渐减小,最小值为100px. 任务提示: (1)多物体运动的定时器需要需要每个物体上同时最多只能开一个定时器 ...

  10. ptmalloc tcmalloc jemalloc 总结的总结 及覆盖原理

    windows下还有 detours库可以hook