首页 > 解决方案 > 为 Func 切换 new Task(()=>{ })

问题描述

回答我的其他一个问题时,有人告诉我,使用 ofnew Task(() => { })不是一个正常的用例。我被建议Func<Task>改用。我试图使这项工作,但我似乎无法弄清楚。(我没有在评论中拖出它,而是在这里提出一个单独的问题。)

我的具体情况是我需要任务在声明时不能正确启动,并且以后能够等待它。

这是一个使用new Task(() => { }). 注意:这非常有效! (除了它使用new Task.)

static async void Main(string[] args)
{
    // Line that I need to swap to a Func<Task> somehow.
    // note that this is "cold" not started task  
    Task startupDone = new Task(() => { });

    var runTask = DoStuff(() =>
    {
        //+++ This is where we want to task to "start"
        startupDone.Start();
    });

    //+++ Here we wait for the task to possibly start and finish. Or timeout.
    // Note that this times out at 1000ms even if "blocking = 10000" below.
    var didStartup = startupDone.Wait(1000);

    Console.WriteLine(!didStartup ? "Startup Timed Out" : "Startup Finished");

    await runTask;

    Console.Read();
}

public static async Task DoStuff(Action action)
{
    // Swap to 1000 to simulate starting up blocking
    var blocking = 1; //1000;
    await Task.Delay(500 + blocking);
    action();
    // Do the rest of the stuff...
    await Task.Delay(1000);
}

我尝试将第二行替换为:

Func<Task> startupDone = new Func<Task>(async () => { });

但随后评论下方的行+++无法正常工作。

startupDone.Start()startupDone.Invoke().

startupDone.Wait需要任务。仅在 lambda 中返回。我不确定如何访问 lambda 之外的任务,所以我可以Wait做到。

如何在我的代码的一部分中使用Func<Task>并启动它并在我的代码Wait的另一部分中为它做 a? (就像我可以用new Task(() => { }))。

标签: c#async-awaittask

解决方案


您发布的代码无法重构以使用 aFunc<Task>而不是冷任务,因为await任务所需的方法(Main方法)与控制任务的创建/启动的方法不同(的 lambda 参数DoStuff方法)。在这种情况下,这可以使Task构造函数的使用合法,这取决于将任务的开始委托给 lambda 的设计决定是否合理。在这个特定的例子中,startupDone它被用作一个同步原语,表示一个条件已经满足并且程序可以继续。这可以通过使用专门的同步原语同样好地实现,例如SemaphoreSlim

static async Task Main(string[] args)
{
    var startupSemaphore = new SemaphoreSlim(0);
    Task runTask = RunAsync(startupSemaphore);
    bool startupFinished = await startupSemaphore.WaitAsync(1000);
    Console.WriteLine(startupFinished ? "Startup Finished" : "Startup Timed Out");
    await runTask;
}

public static async Task RunAsync(SemaphoreSlim startupSemaphore)
{
    await Task.Delay(500);
    startupSemaphore.Release(); // Signal that the startup is done
    await Task.Delay(1000);
}

在我看来SemaphoreSlim,在这种情况下使用 a 更有意义,并使代码的意图更清晰。它还允许await通过 timeout 异步发送信号WaitAsync(Int32),这不是您从Task开箱即用中获得的东西(尽管它是可行的)。

在某些情况下,使用冷任务可能很诱人,但是当您在一两个月后重新访问您的代码时,您会发现自己感到困惑,因为必须处理可能或尚未开始的任务是多么罕见和意外.


推荐阅读