![](/img/trans.png)
[英]When running a async lambda, use Task.Run(func) or new Func<Task>(func)()?
[英]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.