Task.ContinueWith似乎比实际任务更频繁地被调用

时间:2018-01-28 09:01:25

标签: c# task task-parallel-library

首先,这是来自更大的东西,是的,我可以在正常/其他情况下使用await完全避免这种情况。对于任何有兴趣的人,我将在下面解释。

为了跟踪在我的计划继续之前还剩下多少任务,我已经构建了以下内容:

柜台:

private static int counter;

某种方法:

public static void Test()
{
    List<Task> tasks = new List<Task>();
    for (int i = 0; i < 10000; i++)
    {
        TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
        var task = DoTaskWork();
        task.ContinueWith(t => // After DoTaskWork
        {
            // [...] Use t's Result
            counter--; // Decrease counter
            tcs.SetResult(null); // Finish the task that the UI or whatever is waiting for
        });
        tasks.Add(tcs.Task); // Store tasks to wait for
    }
    Task.WaitAll(tasks.ToArray()); // Wait for all tasks that actually only finish in the ContinueWith
    Console.WriteLine(counter);
}

我的超级工作要做:

private static Task DoTaskWork()
{
    counter++; // Increase counter
    return Task.Delay(500);
}

现在,有趣的是,在查看counter时,我最终没有收到数字0。相反,数量随每次执行而变化。为什么是这样?我尝试了各种测试,但找不到不规则的原因。 TaskCompletionSource我认为这是可靠的。感谢。

现在,对于为什么我这样做感兴趣的人:

我需要创建大量任务而不启动它们。为此,我需要使用Task构造函数(一个罕见的用例)。它对Task.Run()的不利之处在于它无法处理await的任何内容,并且它需要从任务中返回类型才能正常运行(因此null作为结果)。因此,我需要解决这个问题。其他想法欢迎......

2 个答案:

答案 0 :(得分:2)

好。我很蠢。仅仅5分钟,我意识到了。

我在锁定辅助对象时做了同样的事情,然后以任何方式更改了计数器,现在它可以工作......

{{1}}

答案 1 :(得分:0)

  

我需要在不启动它们的情况下创建大量任务...因此,我需要解决这个问题。其他想法欢迎......

所以,如果我读得正确,你想要建立一个任务列表,而不是在创建时实际运行它们。您可以通过构建在需要时调用的Func<Task>对象列表来实现:

async Task Main()
{
    // Create list of work to do later
    var tasks = new List<Func<Task>>();

    // Schedule some work
    tasks.Add(() => DoTaskWork(1));
    tasks.Add(() => DoTaskWork(2));

    // Wait for user input before doing work to demonstrate they are not started right away
    Console.ReadLine();

    // Execute and wait for the completion of the work to be done
    await Task.WhenAll(tasks.Select(t => t.Invoke()));
    Console.WriteLine("Ready");
}

public async Task DoTaskWork(int taskNr)
{
    await Task.Delay(100);
    Console.WriteLine(taskNr);
}

这将起作用,即使你像这样使用Task.Run:

public Task DoTaskWork(int taskNr)
{
    return Task.Run(() =>
    {
        Thread.Sleep(100); Console.WriteLine(taskNr);
    });
}

这不是你想要的,你能详细说明你想要创建的任务吗?