C# 这个ASP.NET方法应该是异步的,为什么会被阻塞?
在WCF服务中给定以下方法签名:C# 这个ASP.NET方法应该是异步的,为什么会被阻塞?,c#,asp.net,C#,Asp.net,在WCF服务中给定以下方法签名: public string Query(string request) { using (Log.BeginTimedOperation("Persist request")) { var messageCorrelationId = Guid.NewGuid().ToString(); var payloadURI = PayloadHelper.GetFullPath(messageCorrelationId
public string Query(string request)
{
using (Log.BeginTimedOperation("Persist request"))
{
var messageCorrelationId = Guid.NewGuid().ToString();
var payloadURI = PayloadHelper.GetFullPath(messageCorrelationId);
PayloadHelper.PersistPayloadWithPath(request, payloadURI);
Log.Information("My service request {MessageCorrelationId} {RequestPayloadPath}", messageCorrelationId, payloadURI);
}
// DoWork here, code removed for brevity
return result;
}
以及相应的扩展方法:
public static string GetFullPath(string messageCorrelationId)
{
var folderDate = DateTime.Now.ToString("yyyyMMdd");
var folderHour = DateTime.Now.ToString("HH");
var logFolder = Path.Combine(ConfigurationManager.AppSettings["NetworkFiler"], "Payloads", folderDate, folderHour);
Directory.CreateDirectory(logFolder);
var fileName = $"{messageCorrelationId}-{"MyWCFService"}-{$"{DateTime.Now:yyyy-MM-dd-HH-mm-ss-fff}-{Guid.NewGuid():N}"}.{"xml"}";
return Path.Combine(logFolder, fileName);
}
public static void PersistPayloadWithPath(string text, string path)
{
var task = WriteFileAsync(path, text);
task.GetAwaiter().GetResult();
}
private static async Task WriteFileAsync(string path, string text)
{
try
{
byte[] buffer = Encoding.Unicode.GetBytes(text);
using (var stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true))
{
await stream.WriteAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
}
}
catch (Exception ex)
{
Serilog.Log.Error(ex, "WriteFileAsync");
}
}
例如,如果文件被反病毒(guess)或文件服务器的IO慢度查询,则此代码将被阻止
所以这里是一场大辩论,在ASP.NET中从同步方法调用异步方法。直到今天,我仍然不知道是否有一个可靠的方法来制造火灾和遗忘机制。并不是说我不关心失败,而是应该由catch语句和静态Serilog实例来处理
在写这篇文章的时候,我突然意识到,可能其中的一个问题实际上可能是日志记录器及其围绕文件接收器的异步包装器。。将在几分钟内测试这一点
感谢您对这个混蛋的任何帮助
谢谢,,
斯蒂芬
UPDATE-ASYNC
public async Task<string> QueryAsync(string request)
{
using (Log.BeginTimedOperation("PersistAsync-Request"))
{
var messageCorrelationId = Guid.NewGuid().ToString();
var payloadURI = PayloadHelper.GetFullPath(messageCorrelationId);
await PayloadHelper.PersistPayloadWithPathAsync(request, payloadURI).ConfigureAwait(false);
Log.Information("My service request {MessageCorrelationId} {RequestPayloadPath}", messageCorrelationId, payloadURI);
}
// DoWork here, code removed for brevity
return result;
}
public static string GetFullPath(string messageCorrelationId)
{
var folderDate = DateTime.Now.ToString("yyyyMMdd");
var folderHour = DateTime.Now.ToString("HH");
var logFolder = Path.Combine(ConfigurationManager.AppSettings["NetworkFiler"], "Payloads", folderDate, folderHour);
Directory.CreateDirectory(logFolder);
var fileName = $"{messageCorrelationId}-MyWCFService-{DateTime.Now:yyyy-MM-dd-HH-mm-ss-fff}-{Guid.NewGuid():N}.xml";
return Path.Combine(logFolder, fileName);
}
public static async Task PersistPayloadWithPathAsync(string text, string path)
{
await WriteFileAsync(path, text).ConfigureAwait(false);
}
private static async Task WriteFileAsync(string path, string text)
{
try
{
byte[] buffer = Encoding.Unicode.GetBytes(text);
using (var stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true))
{
await stream.WriteAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
}
}
catch
{
// ignored
}
}
公共异步任务查询同步(字符串请求)
{
使用(Log.BeginTimedOperation(“PersistAsync请求”))
{
var messageCorrelationId=Guid.NewGuid().ToString();
var payloadURI=PayloadHelper.GetFullPath(messageCorrelationId);
Wait PayloadHelper.PersistPayloadWithPathAsync(请求,payloadURI).ConfigureWait(false);
Log.Information(“我的服务请求{MessageCorrelationId}{RequestPayloadPath}”,MessageCorrelationId,payloadURI);
}
//此处为DoWork,为简洁起见删除了代码
返回结果;
}
公共静态字符串GetFullPath(字符串messageCorrelationId)
{
var folderDate=DateTime.Now.ToString(“yyyyMMdd”);
var folderHour=DateTime.Now.ToString(“HH”);
var logFolder=Path.Combine(ConfigurationManager.AppSettings[“NetworkFiler”],“有效载荷”,folderDate,folderHour);
CreateDirectory(logFolder);
var fileName=$“{messageCorrelationId}-MyWCFService-{DateTime.Now:yyyyy-MM-dd-HH-MM-ss-fff}-{Guid.NewGuid():N}.xml”;
返回Path.Combine(logFolder,fileName);
}
公共静态异步任务PersistPayloadWithPathAsync(字符串文本,字符串路径)
{
wait WriteFileAsync(路径,文本)。configurewait(false);
}
专用静态异步任务WriteFileAsync(字符串路径,字符串文本)
{
尝试
{
byte[]buffer=Encoding.Unicode.GetBytes(文本);
使用(var stream=new FileStream(路径,FileMode.Create,FileAccess.Write,FileShare.None,bufferSize:4096,useAsync:true))
{
wait stream.WriteAsync(buffer,0,buffer.Length);
}
}
抓住
{
//忽略
}
}
仍然是随机阻塞,每20-30个请求舔一下问题在于你的
writefinasync
方法。它实际上是同步运行的,直到到达第一个await
(异步/await就是这样工作的)。我相信它挂在新文件流(…)
上
如果您只想启动end forget form同步代码,这就足够了:
public static void PersistPayloadWithPath(string text, string path)
{
Task.Run(async () => await WriteFileAsync(path, text));
}
当您没有异步替代方案时,上面的代码应该会对您有所帮助。然而,正如Gabriel Luci在他的回答中所建议的,您应该使用await File.WriteAllTextAsync(路径,文本)代码>因为它可能针对异步工作进行了优化
您仍然可以使用Task.Run(…)代码>与等待文件.WriteAllTextAsync(路径,文本)代码>在火灾和遗忘场景中
请注意,任务内部的异常(writefleasync
)不会传播到调用线程。在您的情况下,这不会是问题,因为整个WriteFileAsync
方法的主体都在try-catch块中,您可以在其中记录异常
编辑
为了说明线程在异步/等待方法中的行为,请使用以下示例(尝试运行Bar
函数的所有3种方式):
问题在于您的WriteFileAsync
方法。它实际上是同步运行的,直到到达第一个await
(异步/await就是这样工作的)。我相信它挂在新文件流(…)
上
如果您只想启动end forget form同步代码,这就足够了:
public static void PersistPayloadWithPath(string text, string path)
{
Task.Run(async () => await WriteFileAsync(path, text));
}
当您没有异步替代方案时,上面的代码应该会对您有所帮助。然而,正如Gabriel Luci在他的回答中所建议的,您应该使用await File.WriteAllTextAsync(路径,文本)代码>因为它可能针对异步工作进行了优化
您仍然可以使用Task.Run(…)代码>与等待文件.WriteAllTextAsync(路径,文本)代码>在火灾和遗忘场景中
请注意,任务内部的异常(writefleasync
)不会传播到调用线程。在您的情况下,这不会是问题,因为整个WriteFileAsync
方法的主体都在try-catch块中,您可以在其中记录异常
编辑
为了说明线程在异步/等待方法中的行为,请使用以下示例(尝试运行Bar
函数的所有3种方式):
我想它会等待,因为你使用
task.GetAwaiter().GetResult()代码>
如果不需要结果,只需删除这一行即可。它应该很好用
如果您需要结果,您应该使PersistPayloadWithPath
函数也异步。我认为它会等待,因为您使用
task.GetAwaiter().GetResult()代码>
如果不需要结果,只需删除这一行即可。它应该很好用
如果需要结果,您应该使PersistPayloadWithPath
函数也异步。因此它会阻塞新文件流(),构造函数调用一个名为Init()
的方法,该方法实际上会打开文件。因此,它在构造函数中进行I/O,实际上不应该这样做,因为您不能等待它
如果可以,设置useAsync
应使其异步运行。但它可能无法从网络驱动器异步打开文件
所以你最好的办法就是在里面运行它
await File.WriteAllTextAsync(path, text);