[英]C# Async and Sync functions confuse me
我有一个应用程序,其中一个按钮开始创建 XML。 在每个XML 创建结束时, SendInvoice函数发送它,接收响应,一个函数 ( ParseResponse ) 解析响应并执行所需的数据库操作。
这个想法是,当所有的 XML 被创建和发送时,应用程序必须关闭。 问题是我失去了对 async 的控制,并且应用程序似乎在它实际完成所有作业之前就关闭了。 此外,在处理前一个 XML 之前发送 XML。
ParseResponse 函数不是异步的。
这是 SendInvoice 函数。
你能提出任何好的做法吗?
先感谢您。
public async void SendInvoice(string body)
{
Cursor.Current = Cursors.WaitCursor;
var client = new HttpClient();
var queryString = HttpUtility.ParseQueryString(string.Empty);
var uri = "https://xxxx.xxx/SendInvoices?" + queryString;
HttpResponseMessage response;
// Request body
byte[] byteData = Encoding.UTF8.GetBytes(body);
using (var content = new ByteArrayContent(byteData))
{
content.Headers.ContentType = new MediaTypeHeaderValue("application/xml");
response = await client.PostAsync(uri, content);
string responsebody = await response.Content.ReadAsStringAsync();
ParseResponse(response.ToString());
ParseResponse(responsebody);
}
}
其余代码
private void button1_Click(object sender, EventArgs e)
{
For
{
......
SendInvoice(xml)
}
System.Windows.Forms.Application.Exit();
}
由于您是从事件处理程序调用该方法,在这种情况下async void
是可以接受的,请更改您的 Button Click 处理程序方法签名以使用async
,我还在异步方法调用中添加了一些ConfigureAwait(false)
- 最佳实践-call-configureawait-for-all-server-side-code和why-is-writing-configureawaitfalse-on-every-line-with-await-always-recommended :
private async void button1_Click(object sender, EventArgs e)
{
//since you are using a for-loop, I'd suggest adding each Task
//to a List and awaiting all Tasks to complete using .WhenAll()
var tasks = new List<Task>();
FOR
{
......
//await SendInvoice(xml).ConfigureAwait(false);
tasks.Add(SendInvoice(xml));
}
await Task.WhenAll(tasks).ConfigureAwait(false);
System.Windows.Forms.Application.Exit();
}
并更改您的SendInvoice
方法签名以返回Task
public async Task SendInvoice(string body)
{
Cursor.Current = Cursors.WaitCursor;
var client = new HttpClient();
var queryString = HttpUtility.ParseQueryString(string.Empty);
var uri = "https://xxxx.xxx/SendInvoices?" + queryString;
HttpResponseMessage response;
// Request body
byte[] byteData = Encoding.UTF8.GetBytes(body);
using (var content = new ByteArrayContent(byteData))
{
content.Headers.ContentType = new MediaTypeHeaderValue("application/xml");
response = await client.PostAsync(uri, content).ConfigureAwait(false);
string responsebody = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
ParseResponse(response.ToString());
ParseResponse(responsebody);
}
}
我很习惯多线程编程,我花了一些时间来理解异步编程,因为它真的与多线程无关。 它是关于使用单个线程或少量线程做更多事情。
当 CPU 等待处理以外的事情时,异步代码是有益的。 例如:等待网络响应、等待从磁盘读取数据、等待单独的进程(例如数据库服务器)。
它为您正在运行的线程提供了一种在您等待时做其他事情的方法。 C# 使用Task
做到这一点。 任务是一些正在完成的工作,它可以正在运行,也可以正在等待,等待时不需要附加线程。
所有异步函数都必须返回一个 Task 才能有用。 所以你的功能应该是:
public async Task SendInvoice() {
...
编译器使用async
关键字自动将您的函数包装在任务对象中,因此您无需担心很多细节。 您只需在调用另一个异步函数时使用await
。 您可以自己做更多的工作来创建任务或从另一个异步函数返回任务,甚至调用多个异步函数并一起等待所有它们。
例如,如果您的异步方法返回一个值,请使用通用 Task: Task<String>
。
Task
在任务完成之前从异步方法返回。 这就是允许线程被其他东西使用的原因,但它必须回到那个起点,这就是为什么异步编程你会听到“一路异步”。 在它返回到有多个任务需要平衡的调用者之前,它实际上没有任何好处,这通常是您的应用程序或 Web 请求的入口点。
您可以使您的 C# Main
方法异步,但除非您的进程确实在同时执行多项操作,否则这通常无关紧要。 对于 Web 应用程序,它只能处理多个请求。 对于独立的应用程序,这意味着您可以查询多个 API,同时发出多个 Web 请求或数据库查询,并等待它们,只需使用单个线程。 显然,这可以使事情更快(至少在本地,外部资源可能有更多工作要做)。
对于防止程序退出的简单方法,如果您有异步main
,只需await
对SendInvoice
的调用。 如果您的 main 不是异步的,您可以使用以下内容:
SendInvoice().Wait()
或者
SendInvoice().Result
使用Wait()
或Result
将锁定线程直到任务完成。 它通常会使该线程专供该任务使用,因此该线程不能用于任何其他任务。 如果线程池中有更多线程,其他任务可能会继续运行,但通常在单个任务上使用 Wait/Result 会失去异步编程的意义,因此请记住这一点。
编辑 现在您已经发布了您的调用代码,看起来您的调用处于循环中。 这是利用异步调用并立即发送所有发票的好机会。
private async void button1_Click(object sender, EventArgs e)
{
List<Task> tasks = new List<Task>();
FOR
{
......
t = SendInvoice(xml).ConfigureAwait(false);
tasks.Add(t)
}
await Task.WhenAll(tasks).ConfigureAwait(false);
System.Windows.Forms.Application.Exit();
}
这将发送所有发票,然后从处理程序返回,然后在收到所有响应后退出。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.