我需要编写一些异步代码,这些代码本质上是试图重复地与数据库对话并初始化数据库。通常第一次尝试都会失败,因此需要重试。
在过去的日子里,我会使用类似于:
void WaitForItToWork()
{
bool succeeded = false;
while (!succeeded)
{
// do work
succeeded = outcome; // if it worked, mark as succeeded, else retry
Threading.Thread.Sleep(1000); // arbitrary sleep
}
}
我意识到最近在异步模式方面对.NET做了很多更改,所以我的问题是,在探索async
时,这是最好的方法,是否值得?如果是,我如何在async
中实现此模式
更新
为了澄清,我想异步地产生这个工作,这样产生它的方法就不必等待它完成,因为它将在服务的构造函数中产生,所以构造函数必须立即返回。
发布于 2013-09-03 16:46:45
您可以像这样重构该片段:
async Task<bool> WaitForItToWork()
{
bool succeeded = false;
while (!succeeded)
{
// do work
succeeded = outcome; // if it worked, make as succeeded, else retry
await Task.Delay(1000); // arbitrary delay
}
return succeeded;
}
显然,它给您带来的唯一好处是更有效地使用线程池,因为它并不总是需要整个线程来实现延迟。
根据您获取outcome
的方式,使用async/await
可能会有更有效的方法来完成此工作。通常,你可能有像GetOutcomeAsync()
这样的东西,它会以一种自然的方式异步地调用web服务、数据库或套接字,所以你只需要使用var outcome = await GetOutcomeAsync()
。
重要的是要考虑到,编译器会将WaitForItToWork
拆分成多个部分,而await
行中的部分将异步继续。Here's可能是关于它是如何在内部完成的最好的解释。问题是,通常在代码的某个点上,您需要同步异步任务的结果。例如:
private void Form1_Load(object sender, EventArgs e)
{
Task<bool> task = WaitForItToWork();
task.ContinueWith(_ => {
MessageBox.Show("WaitForItToWork done:" + task.Result.toString()); // true or false
}, TaskScheduler.FromCurrentSynchronizationContext());
}
您可以简单地这样做:
private async void Form1_Load(object sender, EventArgs e)
{
bool result = await WaitForItToWork();
MessageBox.Show("WaitForItToWork done:" + result.toString()); // true or false
}
然而,这也会使Form1_Load
成为一种异步方法。
更新
下面是我试图说明 async/await
在这种情况下的实际作用。我创建了同一逻辑的两个版本,WaitForItToWorkAsync
(使用async/await
)和WaitForItToWorkAsyncTap
(使用TAP pattern而不使用async/await
)。与第二个版本不同,第一个版本相当琐碎。因此,虽然async/await
在很大程度上是编译器的语法糖,但它使异步代码更容易编写和理解。
// fake outcome() method for testing
bool outcome() { return new Random().Next(0, 99) > 50; }
// with async/await
async Task<bool> WaitForItToWorkAsync()
{
var succeeded = false;
while (!succeeded)
{
succeeded = outcome(); // if it worked, make as succeeded, else retry
await Task.Delay(1000);
}
return succeeded;
}
// without async/await
Task<bool> WaitForItToWorkAsyncTap()
{
var context = TaskScheduler.FromCurrentSynchronizationContext();
var tcs = new TaskCompletionSource<bool>();
var succeeded = false;
Action closure = null;
closure = delegate
{
succeeded = outcome(); // if it worked, make as succeeded, else retry
Task.Delay(1000).ContinueWith(delegate
{
if (succeeded)
tcs.SetResult(succeeded);
else
closure();
}, context);
};
// start the task logic synchronously
// it could end synchronously too! (e.g, if we used 'Task.Delay(0)')
closure();
return tcs.Task;
}
// start both tasks and handle the completion of each asynchronously
private void StartWaitForItToWork()
{
WaitForItToWorkAsync().ContinueWith((t) =>
{
MessageBox.Show("WaitForItToWorkAsync complete: " + t.Result.ToString());
}, TaskScheduler.FromCurrentSynchronizationContext());
WaitForItToWorkAsyncTap().ContinueWith((t) =>
{
MessageBox.Show("WaitForItToWorkAsyncTap complete: " + t.Result.ToString());
}, TaskScheduler.FromCurrentSynchronizationContext());
}
// await for each tasks (StartWaitForItToWorkAsync itself is async)
private async Task StartWaitForItToWorkAsync()
{
bool result = await WaitForItToWorkAsync();
MessageBox.Show("WaitForItToWorkAsync complete: " + result.ToString());
result = await WaitForItToWorkAsyncTap();
MessageBox.Show("WaitForItToWorkAsyncTap complete: " + result.ToString());
}
关于线程化的几句话。这里没有显式创建的其他线程。在内部,Task.Delay()
实现可能使用池线程(我怀疑它们使用Timer Queues,但在这个特定的示例( WinForms应用程序)中,await
之后的延续将发生在相同的UI线程上。在其他执行环境中(例如控制台应用程序),它可能会在不同的线程上继续。Stephen Cleary所著的this article是理解async/await
线程概念的必读书籍。
发布于 2013-09-03 16:51:45
如果任务是异步的,您可以尝试使用:
async Task WaitForItToWork()
{
await Task.Run(() =>
{
bool succeeded = false;
while (!succeeded)
{
// do work
succeeded = outcome; // if it worked, make as succeeded, else retry
System.Threading.Thread.Sleep(1000); // arbitrary sleep
}
});
}
发布于 2016-05-06 16:03:11
只需提供另一种解决方案
public static void WaitForCondition(Func<bool> predict)
{
Task.Delay(TimeSpan.FromMilliseconds(1000)).ContinueWith(_ =>
{
var result = predict();
// the condition result is false, and we need to wait again.
if (result == false)
{
WaitForCondition(predict);
}
});
}
https://stackoverflow.com/questions/18587628
复制相似问题