我试过阅读异步方法,现在正在尝试创建自己的异步方法。该方法是一个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;
}
谢谢!
答案 0 :(得分:8)
我最近在ThatConference上async
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);
}
}
}