gpt4 book ai didi

c# - 这是编写异步方法的正确方法吗?

转载 作者:太空狗 更新时间:2023-10-30 00:31:25 27 4
gpt4 key购买 nike

我目前正在尝试编写异步代码,但感觉我的代码根本不太正确。

我有以下方法:

public void Commit()
{
_context.SaveChangesToDatabase();
}

不要判断这里的代码,因为这只是示例。另外,如果我使用的是 Entity Framework ,请不要说它们已经与异步方法打包在一起了。我只想在这里了解异步概念。

假设 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 修饰符放在调用此代码的方法上,以便我可以 await this 这意味着继续当前执行并在此方法完成时返回。

最佳答案

火但不要忘记

CommitAsync() 返回一个 Task,但是 Method 完全忽略了 CommitAsync 的返回值——所以是的,代码不会等待,而是继续执行之后的操作。这很糟糕,因为如果 Commit() 抛出异常,您将永远看不到它。理想情况下,每个任务都应该有人在某个地方等待,这样您至少可以看到它是否失败。

假设您没有SaveChangesToDatabase 的异步替代方法,但无论如何您都想在异步上下文中使用它。您可以使用 Task.Run 来创建“伪异步”方法,但不推荐这样做(见下文):

public Task CommitAsync() {
return Task.Run(() => Commit());
}

然后,假设 Method 正在用异步做一些有趣的事情(下面的代码没有做,因为它是那里唯一的异步操作):

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() 确实返回了一些东西(比如受影响的记录数);类似的“假异步”包装器(同样,不推荐 - 见下文)看起来像这样:

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.
}

关于c# - 这是编写异步方法的正确方法吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26503117/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com