11

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

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

谢谢!

4

5 回答 5

9

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

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

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

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

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

必须怀着极大的恐惧来处理服务器上的并行代码。它只在非常有限的场景中是可以接受的。async服务器端的全部要点是每个请求使用更少的线程,当您开始并行化时,您正在做相反的事情:每个请求多个线程。这仅适用于您知道您的用户群非常小的情况;否则,您将扼杀服务器的可扩展性。

于 2013-08-21T20:54:10.827 回答
2

我发现了这篇很棒的 codeproject 详细文章,介绍了如何实现这一点

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

于 2013-08-21T20:52:35.220 回答
1

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

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

于 2013-08-21T20:52:49.033 回答
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);
  }
}

}

于 2017-03-29T23:12:06.283 回答
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);
            }
        }
}
于 2017-03-28T00:17:49.347 回答