Task.Delay(250).Wait()
您知道当您在尝试异步的代码中使用Wait 时,您做错了什么。那是一个无所事事的浪费线程。
以下会更好:
lastTask = lastTask.ContinueWith(t =>
{
// do some task
}).ContinueWith(t => Task.Delay(250)).Unwrap();
ContinueWith 返回一个Task<Task>,Unwrap 调用将其转换为一个Task,当 inner 任务执行时将完成。
现在,为了回答你的问题,我们来看看编译器生成了什么:
public void DoSomeTask()
{
if (this.lastTask == null)
this.lastTask = (Task) Task.FromResult<bool>(false);
// ISSUE: method pointer
// ISSUE: method pointer
this.lastTask = this.lastTask
.ContinueWith(
Program.<>c.<>9__2_0
?? (Program.<>c.<>9__2_0 = new Action<Task>((object) Program.<>c.<>9, __methodptr(<DoSomeTask>b__2_0))))
.ContinueWith<Task>(
Program.<>c.<>9__2_1
?? (Program.<>c.<>9__2_1 = new Func<Task, Task>((object) Program.<>c.<>9, __methodptr(<DoSomeTask>b__2_1))))
.Unwrap();
}
[CompilerGenerated]
[Serializable]
private sealed class <>c
{
public static readonly Program.<>c <>9;
public static Action<Task> <>9__2_0;
public static Func<Task, Task> <>9__2_1;
static <>c()
{
Program.<>c.<>9 = new Program.<>c();
}
public <>c()
{
base.\u002Ector();
}
internal void <DoSomeTask>b__2_0(Task t)
{
}
internal Task <DoSomeTask>b__2_1(Task t)
{
return Task.Delay(250);
}
}
这是在 "show me all the guts" 模式下使用 dotPeek 反编译的。
看这部分:
.ContinueWith<Task>(
Program.<>c.<>9__2_1
?? (Program.<>c.<>9__2_1 = new Func<Task, Task>((object) Program.<>c.<>9, __methodptr(<DoSomeTask>b__2_1))))
ContinueWith 函数被赋予了一个单例委托。所以,那里的任何变量都没有关闭。
现在,有这个功能:
internal Task <DoSomeTask>b__2_1(Task t)
{
return Task.Delay(250);
}
这里的t 是对上一个任务的引用。注意到什么了吗?它从未使用过。 JIT 会将这个本地标记为不可访问,并且 GC 将能够清理它。启用优化后,JIT 将积极标记符合收集条件的本地对象,即使实例方法可以在 GC 收集实例时执行,如果所述实例方法没有t 在待执行的代码中引用this。
现在,最后一件事,Task 类中有 m_parent 字段,这不适合您的场景。但只要你不使用TaskCreationOptions.AttachedToParent 你应该没问题。您可以随时添加 DenyChildAttach 标志以获得额外的安全性和自我记录。
这是function which deals with that:
internal static Task InternalCurrentIfAttached(TaskCreationOptions creationOptions)
{
return (creationOptions & TaskCreationOptions.AttachedToParent) != 0 ? InternalCurrent : null;
}
所以,你在这里应该是安全的。如果您想确定,请在长链上运行内存分析器,然后自己看看。