C# 创建异步网络服务方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18367054/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 12:02:57  来源:igfitidea点击:

Creating an async webservice method

c#.netasync-await

提问by Andreas

I've tried to read up on async methods and am now trying to create my own async method. The method is a webservice call that returns a list of error logs. I'm not sure that I've understood correctly so I thought I'd share my code to see if I should do anything different.

我试图阅读异步方法,现在正在尝试创建自己的异步方法。该方法是一个返回错误日志列表的网络服务调用。我不确定我是否理解正确,所以我想我会分享我的代码,看看我是否应该做一些不同的事情。

All I want the code to do is return a list of errorlogs by calling a method GetAllErrorLogs(), that is a synchronized method. Since it can take a second to fetch all the error logs I want to have the opportunity to do other stuff once I called the GetAllErrorLogs()method. Here is the code.

我想让代码做的就是通过调用方法GetAllErrorLogs()返回错误日志列表,这是一个同步方法。由于获取所有错误日志可能需要一秒钟,因此我希望在调用GetAllErrorLogs()方法后有机会做其他事情。这是代码。

[WebMethod]
public async Task<List<ErrorLog>> GetAllErrorLogs()
{
    List<ErrorLog> errorLogs = new List<ErrorLog>();

    await System.Threading.Tasks.Task.Run(() => {
        errorLogs = ErrorLogRepository.GetAllErrorLogs();
    });


    if (errorLogs == null)
        return new List<ErrorLog>();

    return errorLogs;
}

Thanks!

谢谢!

采纳答案by Stephen Cleary

I recently gave a talk at ThatConferenceon asyncon the server side, and I address this issue in the slides.

我最近在ThatConference上做了一个async关于服务器端的演讲,我在幻灯片中解决了这个问题。

On the server side, you want to avoid the use of Task.Runand other constructs that queue work to the thread pool. As much as possible, keep thread pool threads available for handling requests.

在服务器端,您希望避免使用Task.Run和其他将工作排队到线程池的构造。尽可能保持线程池线程可用于处理请求。

So, ideally your repository would have an asynchronous method GetAllErrorLogsAsync, which would itself be asynchronous. If GetAllErrorLogscannot be asynchronous, then you may as well just call it directly (removing the await Task.Run).

因此,理想情况下,您的存储库将有一个异步方法GetAllErrorLogsAsync,它本身就是异步的。如果GetAllErrorLogs不能是异步的,那么你也可以直接调用它(去掉await Task.Run)。

Since it can take a second to fetch all the error logs I want to have the opportunity to do other stuff once I called the GetAllErrorLogs() method.

由于获取所有错误日志可能需要一秒钟,因此我希望在调用 GetAllErrorLogs() 方法后有机会做其他事情。

If you have a GetAllErrorLogsAsyncavailable, then this can easily be done using Task.WhenAll. However, if GetAllErrorLogsis synchronous, then you can only do this by doing parallel work in your request (e.g., multiple calls to Task.Runfollowed by Task.WhenAll).

如果您有GetAllErrorLogsAsync可用的,那么可以使用Task.WhenAll. 但是,如果GetAllErrorLogs是同步的,那么您只能通过在您的请求中执行并行工作(例如,多次调用Task.Run后跟Task.WhenAll)来做到这一点。

Parallel code on the server must be approached with great trepidation. It is only acceptable in a very limited set of scenarios. The entire point of asyncon the server side is to use fewerthreads per request, and when you start parallelizing, you're doing the opposite: multiplethreads per request. This is only appropriate if you know your user base is very small; otherwise, you'll kill your server scalability.

服务器上的并行代码必须非常谨慎。它只在非常有限的一组场景中是可以接受的。整个点async在服务器端是使用更少的每个请求的线程,当你开始并行化,你正在做相反:每个请求的线程。这仅适用于您知道您的用户群非常小的情况;否则,您将破坏服务器的可扩展性。

回答by Mauricio Gracia Gutierrez

I found this great codeproject detailed article about how to achieve that

我找到了这篇关于如何实现这一目标的优秀代码项目详细文章

http://www.codeproject.com/Articles/600926/Asynchronous-web-services-call-in-ASP-NET

http://www.codeproject.com/Articles/600926/Asynchronous-web-services-call-in-ASP-NET

回答by welegan

**This is potentially wrong, read comments or spinoff question at HttpContext.Current after an await

**这可能是错误的,在等待后阅读HttpContext.Current 的评论或衍生问题

If ErrorLogRepository.GetAllErrorLogs()is not thread-safe, it will cause weird bugs and potentially exception out. Make sure your code is ready for multi-threaded operation before switching to async methods, this is obviously very trivial advice but often overlooked. For example, if you reference HttpContext.Currentin your methods, your code will die in the async method, and sometimes even AFTER the await. The reason is that the code within the async block will potentially be run on a separate thread, which will not have access to the same HttpContext.Currentthread-static property, and awaitgets compiled into two methods. All code before an awaitgets run on one thread, and then calls the code after an await keyword as a continuation, but potentially on yet another thread. So sometimes your code will even work in an async block, only to choke unexpectedly after it gets "out" of the async back to what you think is a synchronous part of your code (but in reality everything after an awaitkeyword is already not guaranteed to be the original thread).

如果ErrorLogRepository.GetAllErrorLogs()不是线程安全的,它将导致奇怪的错误并可能出现异常。在切换到异步方法之前,确保您的代码已准备好进行多线程操作,这显然是非常微不足道的建议,但经常被忽视。例如,如果您HttpContext.Current在方法中引用,您的代码将在异步方法中死亡,有时甚至在await. 原因是异步块中的代码可能会在单独的线程上运行,该线程将无法访问相同的HttpContext.Current线程静态属性,await并被编译为两种方法。之前的所有代码await在一个线程上运行,然后在作为延续的 await 关键字之后调用代码,但可能在另一个线程上运行。所以有时你的代码甚至会在异步块中工作,只是在它“退出”异步回到你认为是代码的同步部分后意外地窒息(但实际上await关键字之后的所有内容都不能保证是原始线程)。

回答by hamish

Here is some production code...

这是一些生产代码...

using System.Web.Http;
using AysncTask = System.Threading.Tasks.Task;

public class myController : ApiControllerBase
{
        [HttpPut]
        [Route("api/cleardata/{id}/{requestId}/")]
        public async AysncTask ClearData(Guid id, Guid requestId)
        {
            try
            {
                await AysncTask.Run(() => DoClearData(id, requestId));
            }
            catch (Exception ex)
            {
                throw new Exception("Exception in myController.ClearData", ex);
            }
        }
}

回答by hamish

Handling Async exceptions is also VERY VERY important.. although this is for a windows console app, the same principles should apply.

处理异步异常也非常重要.. 虽然这是针对 Windows 控制台应用程序,但应该适用相同的原则。

source: https://blogs.msdn.microsoft.com/ptorr/2014/12/10/async-exceptions-in-c/

来源:https: //blogs.msdn.microsoft.com/ptorr/2014/12/10/async-exceptions-in-c/

  using System;
  using System.Runtime.CompilerServices;
  using System.Threading;
  using System.Threading.Tasks;

  namespace AsyncAndExceptions
  {
class Program
{
  static void Main(string[] args)
  {
    AppDomain.CurrentDomain.UnhandledException += (s, e) => Log("*** Crash! ***", "UnhandledException");
    TaskScheduler.UnobservedTaskException += (s, e) => Log("*** Crash! ***", "UnobservedTaskException");

    RunTests();

    // Let async tasks complete...
    Thread.Sleep(500);
    GC.Collect(3, GCCollectionMode.Forced, true);
  }

  private static async Task RunTests()
  {
    try
    {
      // crash
      // _1_VoidNoWait();

      // crash 
      // _2_AsyncVoidAwait();

      // OK
      // _3_AsyncVoidAwaitWithTry();

      // crash - no await
      // _4_TaskNoWait();

      // crash - no await
      // _5_TaskAwait();

      // OK
      // await _4_TaskNoWait();

      // OK
      // await _5_TaskAwait();
    }
    catch (Exception ex) { Log("Exception handled OK"); }

    // crash - no try
    // await _4_TaskNoWait();

    // crash - no try
    // await _5_TaskAwait();
  }

  // Unsafe
  static void _1_VoidNoWait()
  {
    ThrowAsync();
  }

  // Unsafe
  static async void _2_AsyncVoidAwait()
  {
    await ThrowAsync();
  }

  // Safe
  static async void _3_AsyncVoidAwaitWithTry()
  {
    try { await ThrowAsync(); }
    catch (Exception ex) { Log("Exception handled OK"); }
  }

  // Safe only if caller uses await (or Result) inside a try
  static Task _4_TaskNoWait()
  {
    return ThrowAsync();
  }

  // Safe only if caller uses await (or Result) inside a try
  static async Task _5_TaskAwait()
  {
    await ThrowAsync();
  }

  // Helper that sets an exception asnychronously
  static Task ThrowAsync()
  {
    TaskCompletionSource tcs = new TaskCompletionSource();
    ThreadPool.QueueUserWorkItem(_ => tcs.SetException(new Exception("ThrowAsync")));
    return tcs.Task;
  }
  internal static void Log(string message, [CallerMemberName] string caller = "")
  {
    Console.WriteLine("{0}: {1}", caller, message);
  }
}

}

}