我一直在学习HttpClient(通常使用.NET中的API),因此也了解异步编程。我现在仍然很迷路,因此请尝试清除一些内容。我似乎找不到答案的一个问题-异步方法是专门使用async关键字实现的吗?
我了解您可以(理论上)使用异步编程创建同步方法,但是您如何识别呢?例如,来自this link和以下示例:
public string GetReleases(string url)
{
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Add(RequestConstants.UserAgent, RequestConstants.UserAgentValue);
var response = httpClient.GetStringAsync(new Uri(url)).Result;
return response;
}
}
作者说:
为简单起见,我同步实现了
但是我如何识别它是同步的?是否仅因为该方法未使用异步Task定义,例如:
public async Task<string> GetReleases(string url)
这是否意味着在this HttpClient tutorial中,该示例也不是异步的:
// GET: Student
public ActionResult Index()
{
IEnumerable<StudentViewModel> students = null;
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:64189/api/");
//HTTP GET
var responseTask = client.GetAsync("student");
responseTask.Wait();
var result = responseTask.Result;
if (result.IsSuccessStatusCode)
{
var readTask = result.Content.ReadAsAsync<IList<StudentViewModel>>();
readTask.Wait();
students = readTask.Result;
}
else //web api sent error response
{
//log response status here..
students = Enumerable.Empty<StudentViewModel>();
ModelState.AddModelError(string.Empty, "Server error. Please contact administrator.");
}
}
return View(students);
}
总而言之,我的问题是:
答案 0 :(得分:4)
如何识别真正的异步/同步方法?
不能。并不是的。您可以发现可能潜在异步的方法,但是如果不咨询文档或实施这些方法,则几乎无法了解其他方法。
因此,您可以检查方法的返回类型。如果它是void
,则说明您并不多。如果它是Task
,Task<T>
,ValueTask<T>
或任何其他可等待的类型 1 ,则方法可能异步。但请记住,方法签名可能是固定的,因为类型是从基类继承了该方法或正在实现接口。因此,尽管该方法可能具有异步性,但该方法的实际实现可能是完全同步的。
或者,该方法可能具有潜力异步的功能,但可能具有特定的控制流,导致其同步运行 2 。这些可能是例如如果某些条件为真,则该方法已经有了答案,因此可以立即将其返回。否则,它将关闭并执行异步操作-例如。
如果返回类型是不可等待的并且是非无效的,则您可以真正想到的关于该方法的唯一原因是,在返回该点时,它为该返回类型提供了一些值 3 。没有任何理由可以推断该方法可能已开始的其他任何工作-只是如果它已经完成了类似的工作,就不希望您能够发现该工作何时已完成。完成。
如果您正在查看某个方法的实现并询问自己“此实现是否异步”,那么重要的是弄清楚该代码使 控制返回到呼叫者。
控制权何时返回给调用者?
return
await
时,也许 当我们击中await
时,如果我们正在await
进行的等待未完成,我们只会将控制权交还给调用方 4 。因此,我们必须找出可等待的对象来自哪里,如果它来自调用另一个方法,则必须从头开始重新考虑该方法的作用。
如果该方法包含await
,则通常最安全的说法是它可能是异步的-但请记住,以上关于已完成的等待和早期return
的可能性。
如果不是async
/ await
,它还能做什么?好吧,如果它正在与Task
一起工作,那么它可能已经创建了一个或多个这些任务来表示其正在进行的工作。它可能已安排更多代码通过ContinueWith
运行。
如果它不直接与Task
一起使用,还没有调用可能异步的其他东西,没有导致创建新的Thread
并且没有不必要的混淆,它是< em>可能是同步。
例如在我有非常简单的代码并希望避免线程/死锁问题的情况下使用此方法是否可以,因为我现在还不适应异步编程?
问题示例中显示的基于异步模式的同步比使用async
/ await
更容易出现死锁。为什么?因为它们阻塞了当前线程,等待其他事情发生。但是它已锁定的当前线程或资源可能是特殊的-并且它所调用的实际异步任务可能需要获得对同一线程/资源的访问权才能完成。经典僵局。
1 Awaitable是C#使用“鸭子类型”的许多地方之一。如果有一个名为GetAwaiter
的可用方法(实例或扩展名)返回正确形状的类型,则可以等待。因此,尽管事实上您可能永远也看不到一个,但请注意,自定义等待语言在该语言中是可能。
2 想到“热路径”优化。
3 和out
/ ref
参数。当然,如果有的话,它不是通过async
/ await
关键字 实现的异步方法,但它仍然可能具有一些异步行为。
4 如果我们在早先的await
期间已将控制权交还给调用者,则以后await
时我们不会将控制权交还给调用者,但是我们将其返回给不是我们代码的“东西”。
答案 1 :(得分:0)
C#中的异步方法可以返回void
,Task
或Task<T>
,通常应避免使用void
,因为它无法等待。
按照惯例,异步方法应称为DoSomethingAsync()
async
关键字是实现细节,不属于API。仅当在方法主体中使用await
时才需要此关键字。但这不是必须的。您可以简单地委派给另一个异步方法,而无需将该方法标记为async
并使用await
。
因此,应该通过方法名称的后缀Async
来识别“真正的”异步方法,但是,您不能确保实现者实际上使用自然的异步操作,甚至不能同步运行某些部分或整个方法
在该示例中,他通过将.Result
放在GetStringAsync
的末尾来同步制作该方法
答案 2 :(得分:0)
如果方法返回Task
或Task<T>
(事件处理程序为void除外),则可以等待它,因此它可以是异步的。 async
关键字仅表示它可能await
在某个地方等待另一个等待。根据控制流,async
可能会返回而实际上没有等待任何东西。
异步编程并不是什么新鲜事物,它以回调,调用等多种形式存在。
您提供的示例未正确使用异步等待模式。 Microsoft提供了命名约定(异步后缀)和Task<T>
,Task
作为异步编程的类型。因此,如果看到某个方法返回Task<T>
或Task
,并且方法名称后缀为“ Async”,则可以认为它是异步的。尽管编译器不需要后缀,但有助于将其与同步对象区分开。 (Read
与ReadAsync
)
它们是不好的例子,应该将那些动作标记为异步,并且所有异步方法都应等待结果。在某些控制台程序中,main可能不是异步的。