C# 创建异步webservice方法

C# 创建异步webservice方法,c#,.net,async-await,C#,.net,Async Await,我已经尝试阅读了异步方法,现在正尝试创建自己的异步方法。该方法是返回错误日志列表的webservice调用。我不确定我是否理解正确,所以我想我应该分享我的代码,看看我是否应该做些不同的事情 我只希望代码通过调用方法GetAllErrorLogs()返回一个错误日志列表,这是一个同步方法。因为获取所有错误日志可能需要一秒钟的时间,所以我希望在调用GetAllErrorLogs()方法后有机会做其他事情。这是代码 [WebMethod] public async Task<List<Er

我已经尝试阅读了异步方法,现在正尝试创建自己的异步方法。该方法是返回错误日志列表的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;
}
[WebMethod]
公共异步任务GetAllErrorLogs()
{
List errorLogs=新列表();
等待System.Threading.Tasks.Task.Run(()=>{
errorLogs=ErrorLogRepository.GetAllErrorLogs();
});
if(errorLogs==null)
返回新列表();
返回错误日志;
}

谢谢

我发现了这篇关于如何实现这一目标的伟大代码项目详细文章


**这可能是错误的,请阅读以下评论或附带问题:

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

我最近在,我在幻灯片中谈到了这个问题

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

因此,理想情况下,您的存储库应该有一个异步方法
GetAllErrorLogsAsync
,它本身就是异步的。如果
GetAllErrorLogs
不能是异步的,那么您可以直接调用它(删除
wait任务。运行

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

如果您有可用的
GetAllErrorLogsAsync
,则可以使用
Task.whalll
轻松完成此操作。但是,如果
GetAllErrorLogs
是同步的,那么您只能通过在请求中执行并行工作(例如,多次调用
Task.Run
,然后调用
Task.whalll
)来实现这一点


服务器上的并行代码必须以极大的恐惧来处理。这仅在非常有限的一组场景中是可以接受的。服务器端的
async
的全部要点是每个请求使用较少的线程,而当您开始并行化时,您的做法正好相反:每个请求使用多个线程。只有当你知道你的用户群很小时,这才是合适的;否则,您将破坏服务器的可扩展性。

以下是一些生产代码

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

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

资料来源:


}

我认为在服务器端使用async/await没有什么好处。您只需对同一事物使用更多线程即可。@I4V:
async
服务器端可以显著减少每个请求使用的线程数(假设代码是自然异步的,而不是伪异步的,例如
Task.Run
)。因此,异步服务器能够更好地扩展,通常在10-100倍左右。。。我在微软的一个校园参加了一个培训课程,在那里我们被告知,只要将服务器上运行的所有代码异步化,就可以大幅提高相同硬件的容量。纯粹是因为,当一个方法正在等待子调用的响应时,主线程被释放去做其他工作。。。ie处理其他并发的web请求。优点是多任务。这个答案有很多错误信息<此处不需要代码>未观察到的异常(
等待
将通过WebAPI方法正确传播
GetAllErrorLogs
中的任何异常)
HttpContext.Current
在默认情况下被正确地传播到处理异步请求的所有线程(即,
返回错误日志
行有一个完全有效的
HttpContext.Current
)。关于等待,你说得对,我错读了那部分。它只会在任务的情况下不被传播。运行被用作火,然后忘记。但是,HttpContext.Current在等待后肯定无效,除非WebAPI已覆盖默认的同步上下文行为。WebAPI不提供同步上下文,但ASP.NET提供。因此,
HttpContext.Current
wait
@StephenCleary之后是完全有效的,WebAPI不提供同步上下文-hmm,在任何情况下,它仍然是Web API的
AspNetSynchronizationContext
?下面是一个例子,我要求OP
Debug.Print(SynchronizationContext.Current.GetType().Name)
,结果是
AspNetSynchroniz
  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);
  }
}