我在过去看到人们在线程ID上关联日志条目;一个不完美的解决方案,但在许多情况下都有效。开始在c#中进行异步编程之后,我很好奇处理多个异步操作的日志记录的最佳方法是什么。举个例子:
async Task<string> GetSongNameAsync(byte [] songBytes, AuthInfo auth)
{
Log("Received request from user " + auth.User);
if (!await Authentication.HasPremiumAccountAsync(auth)) // More logging in here
{
return "Error: Your subscription does not support this feature";
}
Song song = await SongIdentifier.IdentifyAsync(songBytes); // Even more logging here
if (song != null)
{
return song.ToString()
}
return new Song() {Name = "Never Gonna Give You Up", Artist = "Rick Astley", Notes = "You just got rick-rolled!"};
}
如果auth逻辑或歌曲ID逻辑中存在错误,我想将错误与特定请求相关联,这样我就可以查询我的日志记录数据库并查看请求的处理顺序,也许它失败的原因。我可以开始传递一个Guid或类似的东西,并在消息中包含它,但是当我真的希望它在'thread local storage'的异步等效时,让每个构造函数和对象都暴露这个值似乎有些麻烦。
答案 0 :(得分:0)
您可以使用Task.Id
和静态Task.CurrentId
,方式与Thread.Id
类似,但它并不像async \ await那样真正发挥作用。 here。
我认为最好的方法是以独特的方式识别您收到的每个请求,方法是将requestId附加到它(或基类),使用基础设施提供的东西(在WCF中这将是OperationContext.Current.IncomingMessageHeaders.MessageId
) ,或者在收到请求时使用确切的刻度(可能带有线程ID)。
关于你的评论..
你可以:
public static class MagicalHelpper
{
private static ConditionalWeakTable<object, int> _registry = new ConditionalWeakTable<object, int>();
public void GetId(object obj)
{
int result = _registry.GetValue(obj, ((object key)=> DateTime.Now.Ticks +
Thread.CurrentThread.ManagedThreadId));
return result;
}
}
这是在没有编译器的情况下编写的......
现在,只要你愿意,你就可以去MagicalHelpper.GetId(something)
,只记得坚持使用相同的请求对象......
这应该是完全线程安全的,并且不会导致任何内存泄漏。 ConditionalWeakTable文档:http://msdn.microsoft.com/en-us/library/dd287757.aspx
答案 1 :(得分:0)
有一篇关于此主题的大型MSDN文章here。这里没有简单的解决方案,但它描述了使用ExecutionContext
和线程本地存储执行此操作的一种方法。这里有一些示例代码。注意事项虽然适用。
如果您正在运行。{4.5},还有this question的替代答案