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
Creating an async webservice method
提问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 async
on 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.Run
and 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 GetAllErrorLogs
cannot 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 GetAllErrorLogsAsync
available, then this can easily be done using Task.WhenAll
. However, if GetAllErrorLogs
is synchronous, then you can only do this by doing parallel work in your request (e.g., multiple calls to Task.Run
followed 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 async
on 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.Current
in 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.Current
thread-static property, and await
gets compiled into two methods. All code before an await
gets 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 await
keyword 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);
}
}
}
}