我目前正在尝试编写异步代码,我觉得我的代码根本不太正确。
我有以下方法:
public void Commit()
{
_context.SaveChangesToDatabase();
}
不要在这里判断代码,因为这只是样本。另外,如果我使用实体框架,那么它们已经与Async方法打包在一起了。我只想在这里理解异步概念。
让我们说方法SaveChangesToDatabase
确实需要几秒钟才能完成。
现在,我不想等待它,所以我创建了一个异步方法:
public async Task CommitAsync()
{
await Task.Run(() => Commit());
}
这是否意味着如果我有方法:
public void Method()
{
// Operation One:
CommitAsync();
// Operation Two.
}
这是否意味着我的代码将在CommitAsync()
完成之前执行?
如果没有,请指导我正确的方向。
更新
基于此处的评论,我忽略了我的异步方法结果,这种实现更好吗?
public Task<TaskResult> CommitAsync()
{
var task = new Task<TaskResult>(() =>
{
try { Commit(); }
catch (Exception ex)
{
return new TaskResult
{
Result = TaskExceutionResult.Failed,
Message = ex.Message
};
}
return new TaskResult { Result = TaskExceutionResult.Succeeded };
});
task.Start();
return task;
}
这确实意味着我需要在调用此代码的方法上放置async
修饰符,以便我可以等待这意味着继续当前执行并在此方法完成时返回。
答案 0 :(得分:8)
CommitAsync()
会返回Task
,但Method
会完全忽略CommitAsync
的返回值 - 所以是的,代码不会等待,只需继续使用&# 39;之后。这很糟糕,因为如果Commit()
抛出异常,你将永远不会看到它。理想情况下,每个任务都应该由某人在某个地方等待,因此您至少可以看到它是否失败。
让我们说你没有SaveChangesToDatabase
的异步替代品,但无论如何你想在异步上下文中使用它。您可以使用Task.Run
来创建&#34;假异步&#34;方法,但不建议这样做(见下文):
public Task CommitAsync() {
return Task.Run(() => Commit());
}
然后,假设Method
正在使用async做一些有趣的事情(下面的代码不那么做,因为它是那里唯一的异步操作):
public async Task MethodAsync() {
// Operation One:
await CommitAsync();
// Operation Two.
}
假设您不想等待,但如果任务失败,您确实想要做某事,您可以使用单独的方法:
public void Method() {
// Operation One:
var _ = TryCommitAsync();
// Operation Two.
}
private async Task TryCommitAsync()
{
try
{
await CommitAsync();
}
catch (Exception ex)
{
Console.WriteLine(
"Committing failed in the background: {0}",
ex.Message
);
}
}
假设.Commit()
确实返回了某些内容(比如受影响的记录数);类似的&#34;假异步&#34;包装(再次,不推荐 - 见下文)看起来像这样:
public Task<int> CommitAsync() {
return Task.Run(() => Commit());
}
如果您想要此结果,可以立即等待任务:
public async Task MethodAsync() {
// Operation One:
int recordsAffected = await CommitAsync();
// Operation Two.
}
或者,如果您不立即需要,请在执行此操作时使用await
:
public async Task MethodAsync() {
// Operation One:
Task<int> commit = CommitAsync();
// Operation Two.
// At this point I'd really like to know how many records were committed.
int recordsAffected = await commit;
}
一般情况下,您不想编写CommitAsync()
之类的包装器,因为它们mislead callers into thinking code will be asynchronous when it isn't really,除了不阻塞之外几乎没有什么好处(在UI代码中仍然有用,但不是非常好的异步代码,它不需要使用工作线程来处理所有事情)。换句话说,您应该在方法的调用中使用Task.Run
,而不是方法的实现。
所以,作为一种习惯,不要为每个同步方法编写CommitAsync
这样的包装器 - 而是要创建一个使用底层的异步支持的真CommitAsync
库/框架(SqlCommand.ExecuteReaderAsync()
,等等。)
如果您别无选择且必须使用Task.Run
,则相应的用法看起来更像:
// This method is in the UI layer.
public async Task MethodAsync() {
// Operation One:
// Commit() is a method in the DA layer.
await Task.Run(() => Commit());
// Operation Two.
}
答案 1 :(得分:0)
这里
http://channel9.msdn.com/events/TechEd/NorthAmerica/2013/DEV-B318#fbid=
是关于如何使用异步的一个很好的解释,以及为什么你应该避免&#34; async over sync&#34;,这就是你现在正在做的事情
public Task CommitAsync() {
return Task.Run(() => Commit());
}
在某些情况下,您可以从中受益,但如果您要将此作为库的一部分提供,则不是一个好主意。 如果这个代码只是由您的应用程序使用,并且您确定自己在做什么并且没有在异步方法中调用异步方法,那就去做吧