繁体   English   中英

为 Func 切换 new Task(()=&gt;{ })<task></task>

[英]Switch new Task(()=>{ }) for Func<Task>

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

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

这是一个使用new Task(() => { })的 LinqPad 示例。 注意:这非常有效! (除了它使用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.Invoke() 交换了startupDone.Start() startupDone.Invoke()

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

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

您发布的代码不能重构为使用Func<Task>而不是冷任务,因为需要await任务的方法( Main方法)与控制任务创建/启动的方法不同( DoStuff方法的 lambda 参数)。 在这种情况下,这可以使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更有意义,并使代码的意图更清晰。 它还允许使用超时WaitAsync(Int32)异步await信号,这不是您从开箱即用的Task中获得的东西(尽管它是可行的)。

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

首先,您的“稍后再做” object 的类型将变为Func<Task> 然后,当任务启动时(通过调用函数),你会得到一个代表操作的Task

static async void Main(string[] args)
{
  Func<Task> startupDoneDelegate = async () => { };
  Task startupDoneTask = null;

  var runTask = await DoStuff(() =>
  {
    startupDoneTask = startupDoneDelegate();
  });

  var didStartup = startupDoneTask.Wait(1000);

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

在处理任何异步或代表潜在异步行为的任何类型(例如Task )时,我总是尽我最大的努力从不出现阻塞行为。 您可以稍微修改您的DoStuff以方便等待您的Action


static async void Main(string[] args)
{
    Func<CancellationToken,Task> startupTask = async(token)=>
    {
        Console.WriteLine("Waiting");
        await Task.Delay(3000, token);
        Console.WriteLine("Completed");
    };
    using var source = new CancellationTokenSource(2000);
    var runTask = DoStuff(() => startupTask(source.Token), source.Token);
    var didStartup = await runTask;
    Console.WriteLine(!didStartup ? "Startup Timed Out" : "Startup Finished");
    Console.Read();
}

public static async Task<bool> DoStuff(Func<Task> action, CancellationToken token)
{
    var blocking = 10000;
    try
    {
        await Task.Delay(500 + blocking, token);
        await action();
    }
    catch(TaskCanceledException ex)
    {
        return false;
    }
    await Task.Delay(1000);
    return true;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM