Com*_*ity 4 c# asynchronous async-await c#-5.0
我目前正在尝试编写异步代码,我觉得我的代码根本不太正确.
我有以下方法:
public void Commit()
{
_context.SaveChangesToDatabase();
}
Run Code Online (Sandbox Code Playgroud)
不要在这里判断代码,因为这只是样本.此外,不要说如果我使用实体框架,它们已经与Async方法打包在一起.我只是想了解这里的异步概念.
假设该方法SaveChangesToDatabase确实需要几秒钟才能完成.现在,我不想等待它,所以我创建了一个异步方法:
public async Task CommitAsync()
{
await Task.Run(() => Commit());
}
Run Code Online (Sandbox Code Playgroud)
这是否意味着如果我有一个方法:
public void Method()
{
// Operation One:
CommitAsync();
// Operation Two.
}
Run Code Online (Sandbox Code Playgroud)
这是否意味着我的操作二代码将在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;
}
Run Code Online (Sandbox Code Playgroud)
这确实意味着我需要将async修饰符放在调用此代码的方法上,以便我可以等待这意味着继续当前执行并在此方法完成时返回.
CommitAsync()返回a Task,但完全Method忽略了返回值CommitAsync- 所以是的,代码不会等待,而只是继续之后的内容.这很糟糕,因为如果Commit()抛出异常,你将永远不会看到它.理想情况下,每个任务都应该由某人在某个地方等待,因此您至少可以看到它是否失败.
假设您没有异步替代SaveChangesToDatabase,但无论如何您都希望在异步上下文中使用它.您可以使用Task.Run创建"假异步"方法,但不建议这样做(见下文):
public Task CommitAsync() {
return Task.Run(() => Commit());
}
Run Code Online (Sandbox Code Playgroud)
然后,假设Method正在做一些有趣的异步(下面的代码没有做,因为它是那里唯一的异步操作):
public async Task MethodAsync() {
// Operation One:
await CommitAsync();
// Operation Two.
}
Run Code Online (Sandbox Code Playgroud)
假设您不想等待,但如果任务失败,您确实想要执行某些操作,则可以使用单独的方法:
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
);
}
}
Run Code Online (Sandbox Code Playgroud)
我们假设.Commit()确实返回了一些东西(比如受影响的记录数); 一个类似的"假异步"包装器(再次,不推荐 - 见下文)看起来像这样:
public Task<int> CommitAsync() {
return Task.Run(() => Commit());
}
Run Code Online (Sandbox Code Playgroud)
如果您想要此结果,可以立即等待任务:
public async Task MethodAsync() {
// Operation One:
int recordsAffected = await CommitAsync();
// Operation Two.
}
Run Code Online (Sandbox Code Playgroud)
或者,如果您不立即需要,请在以下情况下使用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;
}
Run Code Online (Sandbox Code Playgroud)
一般来说,你不想编写包装器,CommitAsync()因为它们误导了调用者,认为代码在异常时是异步的,除了不阻塞之外几乎没有什么好处(这在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.
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
606 次 |
| 最近记录: |