创建异步Web服务方法

时间:2013-08-21 20:28:45

标签: c# .net async-await

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

我想要的代码是通过调用方法 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;
}

谢谢!

5 个答案:

答案 0 :(得分:8)

我最近在ThatConferenceasync on the server side发表了演讲,我在幻灯片中解决了这个问题。

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

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

  

由于可能需要一秒钟才能获取所有错误日志,因此我希望在调用GetAllErrorLogs()方法后可以执行其他操作。

如果您有GetAllErrorLogsAsync可用,则可以使用Task.WhenAll轻松完成此操作。但是,如果GetAllErrorLogs是同步的,那么您只能在请求中执行并行工作(例如,多次调用Task.Run后跟Task.WhenAll)。

必须非常谨慎地处理服务器上的并行代码。它只能在非常有限的场景中接受。服务器端async的整个要点是每个请求使用更少的线程,当你开始并行化时,你正在做相反的事情:多个线程根据要求。这只适用于您知道您的用户群非常小的情况;否则,你将扼杀你的服务器可扩展性。

答案 1 :(得分:2)

我找到了这篇很棒的代码项目详细文章,介绍了如何实现这个目标

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

答案 2 :(得分:1)

**这可能是错误的,请在HttpContext.Current after an await

阅读评论或衍生问题

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

答案 3 :(得分:0)

这是一些生产代码......

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);
            }
        }
}

答案 4 :(得分:0)

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

来源: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);
  }
}

}