链接任务有延迟

时间:2016-05-02 20:32:09

标签: 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());
} 

我的问题是,如果我做这样的事情,创建可能很长的任务链就会处理旧任务,或者它们最终会永远存在,因为ContinueWith将最后一个任务作为参数(所以这是一个关闭)。如果是这样,我如何在避免此问题的同时将任务链接起来?

有更好的方法吗?

3 个答案:

答案 0 :(得分:2)

  

如果我做这样的事情,创建潜在的长任务链将是旧的任务被处理

任务不需要明确处理,因为它们不包含非托管资源。

  

他们最终会永远坚持下去,因为ContinueWith将最后一个任务作为一个参数(所以它是一个闭包)

不是一个闭包。闭包是一种匿名方法,在其正文中使用来自匿名方法范围之外的变量。你没有这样做,所以你不会关闭它。但是,每个Task都有一个跟踪其父级的字段,因此如果您使用此模式,托管的Task对象仍然可以访问。

答案 1 :(得分:2)

看看source code of the ContinuationTaskFromTask class。它具有以下代码:

internal override void InnerInvoke()
{
    // Get and null out the antecedent.  This is crucial to avoid a memory
    // leak with long chains of continuations.
    var antecedent = m_antecedent;
    Contract.Assert(antecedent != null, 
        "No antecedent was set for the ContinuationTaskFromTask.");
    m_antecedent = null;

m_antecedent是包含对先行提问的引用的字段。这里的开发人员明确地将它设置为null(在不再需要它之后)以确保没有长连续链的内存泄漏,我想这是你的关注。

答案 2 :(得分:1)

Task.Delay(250).Wait()

当你在尝试异步的代码中使用Wait时,知道你做错了什么。这是一个浪费的线程什么都不做。

以下情况会好得多:

lastTask = lastTask.ContinueWith(t =>
{
    // do some task
}).ContinueWith(t => Task.Delay(250)).Unwrap();

ContinueWith返回Task<Task>Unwrap调用会将其转换为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); } } 将完成。< / p>

现在,为了回答你的问题,我们来看看编译器生成的内容:

.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))))

这是在“告诉我所有胆量”模式中使用dotPeek反编译的。

看看这部分:

ContinueWith

internal Task <DoSomeTask>b__2_1(Task t) { return Task.Delay(250); } 函数被赋予一个单独的委托。所以,那里的任何变量都没有关闭。

现在,有这个功能:

t

这里的this是对上一个任务的引用。注意什么?它从未使用过。 JIT会将此本地标记为无法访问,GC将能够清除它。在启用优化的情况下,JIT将积极地标记符合收集条件的本地人,即使实时方法可以执行正在由GC收集实例,如果所述实例方法没有' t在要执行的代码中引用Task

现在,最后一件事,TaskCreationOptions.AttachedToParent类中有m_parent字段,这对您的方案不利。但只要你没有使用DenyChildAttach,你应该没问题。您可以随时添加internal static Task InternalCurrentIfAttached(TaskCreationOptions creationOptions) { return (creationOptions & TaskCreationOptions.AttachedToParent) != 0 ? InternalCurrent : null; } 标志以获得额外的安全性和自我记录。

这是function which deals with that

curl xxx://udacity.github.io/ud595-shell/stuff.zip -o things.zip

所以,你应该在这里安全。如果您想确定,请在长链上运行内存分析器,并亲眼看看。