Mat*_*and 7 c# task-parallel-library
我需要跟踪一个任务,并可能在一段时间后排队另一个任务,所以我想这样做的方式看起来像这样:
private Task lastTask;
public void DoSomeTask()
{
if (lastTask == null)
{
lastTask = Task.FromResult(false);
}
lastTask = lastTask.ContinueWith(t =>
{
// do some task
}).ContinueWith(t => Task.Delay(250).Wait());
}
Run Code Online (Sandbox Code Playgroud)
我的问题是,如果我做这样的事情,创建潜在的长链任务就会处理旧任务,或者它们最终会永远存在,因为ContinueWith它将最后一个任务作为参数(所以它是一个闭包).如果是这样,我如何在避免此问题的同时链接任务?
有一个更好的方法吗?
Task.Delay(250).Wait()
Run Code Online (Sandbox Code Playgroud)
当您在尝试异步的代码中使用时,您知道自己做错了。Wait这是一个无所事事的浪费线程。
下面的内容会好得多:
lastTask = lastTask.ContinueWith(t =>
{
// do some task
}).ContinueWith(t => Task.Delay(250)).Unwrap();
Run Code Online (Sandbox Code Playgroud)
ContinueWith返回 a Task<Task>,调用Unwrap将其转换为 a ,它将在内部Task任务完成时完成。
现在,为了回答您的问题,让我们看一下编译器生成的内容:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
这是用 dotPeek 在“show me all the guts”模式下反编译的。
看这部分:
.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))))
Run Code Online (Sandbox Code Playgroud)
该ContinueWith函数被赋予一个单例委托。因此,那里没有任何变量。
现在,有这个功能:
internal Task <DoSomeTask>b__2_1(Task t)
{
return Task.Delay(250);
}
Run Code Online (Sandbox Code Playgroud)
这里t是对上一个任务的引用。注意到什么了吗?它从未被使用过。JIT 会将这个本地标记为无法访问,并且 GC 将能够清理它。启用优化后,JIT 将积极标记符合收集条件的本地变量,甚至在GC 收集实例时this可以执行实例方法(如果所述实例方法未在剩下的代码中引用)执行。
m_parent现在,最后一件事是,类中有一个字段Task,这对您的场景不利。但只要你不使用TaskCreationOptions.AttachedToParent就应该没问题。您始终可以添加该DenyChildAttach标志以获得额外的安全性和自我记录。
这是处理这个问题的函数:
internal static Task InternalCurrentIfAttached(TaskCreationOptions creationOptions)
{
return (creationOptions & TaskCreationOptions.AttachedToParent) != 0 ? InternalCurrent : null;
}
Run Code Online (Sandbox Code Playgroud)
所以,你在这里应该是安全的。如果您想确定,请在长链上运行内存分析器,然后亲自查看。