C# 最好的异步 while 方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18587628/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Best asynchronous while method
提问by Chris
I need to write some asynchronous code that essentially attempts to repeatedly talk to and initialise a database. Quite often the first attempt will fail hence the requirement for it to retry.
我需要编写一些异步代码,这些代码本质上是尝试重复与数据库对话并初始化数据库。通常第一次尝试会失败,因此需要重试。
In days of old I would have used a pattern similar to:
在过去的日子里,我会使用类似于以下的模式:
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
}
}
I realise a lot of changes have been made recently to .NET with regards to async patterns so my question really is this the best method to use or is it worth while exploring the async
stuff and if so how do I implement this pattern in async
?
我意识到最近在异步模式方面对 .NET 进行了很多更改,所以我的问题确实是最好的使用方法,或者是否值得探索这些async
东西,如果是这样,我如何在 .NET 中实现这种模式async
?
Update
更新
Just to clarify, I want to spawn this work asynchronously so that the method which spawns it does not have to wait for it to finish as it will be spawned in the constructor of a service so the constructor must return instantly.
只是为了澄清,我想异步生成这项工作,以便生成它的方法不必等待它完成,因为它将在服务的构造函数中生成,因此构造函数必须立即返回。
采纳答案by noseratio
You could refactor that fragment like this:
你可以像这样重构那个片段:
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;
}
Apparently, the only benefit it would give you is more efficient use of thread pool, because it doesn't always take a whole thread to make the delay happen.
显然,它会给您带来的唯一好处是更有效地使用线程池,因为它并不总是需要整个线程来使延迟发生。
Depending on how you obtain outcome
, there may be much more efficient ways to get this job done using async/await
. Often you may have something like GetOutcomeAsync()
which would make a web service, database or socket call asynchronously in a natural way, so you'd just do var outcome = await GetOutcomeAsync()
.
根据您的获取outcome
方式,使用async/await
. 通常,您可能会使用类似的东西GetOutcomeAsync()
以自然的方式异步调用 Web 服务、数据库或套接字,因此您只需执行var outcome = await GetOutcomeAsync()
.
It's important to take into account that WaitForItToWork
will be split into parts by compiler and the part from await
line will be continued asynchronously. Here'sperhaps the best explanation on how it's done internally. The thing is, usually at some point of your code you'd need to synchronize on the result of the async task. E.g.:
重要的是要考虑到它WaitForItToWork
会被编译器分成几部分,而行中的部分await
将异步继续。这可能是关于它如何在内部完成的最好解释。问题是,通常在代码的某个时刻,您需要对异步任务的结果进行同步。例如:
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());
}
You could have simply done this:
你可以简单地这样做:
private async void Form1_Load(object sender, EventArgs e)
{
bool result = await WaitForItToWork();
MessageBox.Show("WaitForItToWork done:" + result.toString()); // true or false
}
That would however make Form1_Load
an async method too.
然而,这也会成为Form1_Load
一个异步方法。
[UPDATE]
[更新]
Below is my attempt to to illustrate what async/await
actually does in this case.I created two versions of the same logic, WaitForItToWorkAsync
(using async/await
) and WaitForItToWorkAsyncTap
(using TAP patternwithout async/await
). The frist version is quite trivial, unlike the second one. Thus, while async/await
is largely the compiler's syntactic sugar, it makes asynchronous code much easier to write and understand.
下面是我试图说明async/await
在这种情况下实际执行的操作。我创建了相同逻辑的两个版本,WaitForItToWorkAsync
(使用async/await
)和WaitForItToWorkAsyncTap
(使用不带 的TAP 模式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());
}
A few words on threading. There is no additional threads explicitly created here. Internally, Task.Delay()
implementation may use pool threads (I suspect they use Timer Queues), but in this particular example (a WinForms app), the continuation after await
will happen on the same UI thread. In other execution environments (e.g. a console app), it might continue on a different thread. IMO, this articleby Stephen Cleary is a must-read to understand async/await
threading concepts.
关于线程的几句话。这里没有显式创建额外的线程。在内部,Task.Delay()
实现可能使用池线程(我怀疑他们使用Timer Queues),但在这个特定示例(WinForms 应用程序)中,后续操作await
将发生在同一个 UI 线程上。在其他执行环境(例如控制台应用程序)中,它可能会在不同的线程上继续。IMO,Stephen Cleary 的这篇文章是理解async/await
线程概念的必读之物。
回答by Alessandro D'Andria
If the task is asynchronous you can try with:
如果任务是异步的,您可以尝试:
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
}
});
}
回答by Konstantin Spirin
You don't really need WaitItForWork
method, just await for a database initialization task:
你真的不需要WaitItForWork
方法,只需要等待数据库初始化任务:
async Task Run()
{
await InitializeDatabase();
// Do what you need after database is initialized
}
async Task InitializeDatabase()
{
// Perform database initialization here
}
If you have multiple pieces of code that call to WaitForItToWork
then you need to wrap database initialization into a Task
and await it in all workers, for example:
如果您有多段代码调用,WaitForItToWork
那么您需要将数据库初始化包装到 aTask
并在所有工作人员中等待它,例如:
readonly Task _initializeDatabaseTask = InitializeDatabase();
async Task Worker1()
{
await _initializeDatabaseTask;
// Do what you need after database is initialized
}
async Task Worker2()
{
await _initializeDatabaseTask;
// Do what you need after database is initialized
}
static async Task InitializeDatabase()
{
// Initialize your database here
}
回答by user2986287
Just provide another solution
只是提供另一个解决方案
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);
}
});
}