繁体   English   中英

C# Async 和 Sync 函数让我很困惑

[英]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-codewhy-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 ,只需awaitSendInvoice的调用。 如果您的 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM