我一直在为一些较大的.NET解决方案实现[<Trace>]
属性,该属性将使可配置分析易于添加到任何重要的函数/方法中。我正在使用Fody和MethodBoundaryAspect来拦截每个函数的进入和退出并记录指标。这对于同步函数非常有用,对于返回Task
的方法,有一个Task.ContinueWith
可行的解决方案,但是对于F#异步返回函数,MethodBoundaryAspect中的OnExit
会尽快运行返回异步(而不是实际执行异步时)。
为了为F#异步返回功能捕获正确的指标,我试图提出一种与使用Task.ContinueWith
等效的解决方案,但是我能想到的最接近的方法是创建一个新的Async来绑定第一个,运行度量捕获功能,然后返回原始结果。由于我截取的F#异步返回值仅以obj
的形式显示,因此,这又使情况变得更加复杂,此后我必须反思地进行所有操作,因为没有{{1}的非通用版本}就像Async
一样,我可以在不知道确切的返回类型的情况下使用它。
到目前为止,我最好的解决方案大致如下:
Task
不幸的是,该解决方案不仅非常凌乱,而且我相信异步计算的反射结构会增加大量的开销,尤其是当我尝试跟踪在循环中调用或具有深度嵌套的异步调用。在实际计算了异步计算后,是否有更好的方法立即获得运行给定功能的相同结果?
答案 0 :(得分:1)
您可能需要这样的东西
cmake --help
请考虑当函数返回异步并不意味着异步已经启动时。异步更像是一个函数,它可以被调用多次或根本不被调用。这意味着您还需要在<configuration>
<webXml>WEB-INF/glassfish-web.xml</webXml>
</configuration>
方法中检查返回值是否为Async。
答案 1 :(得分:0)
按照@AMieres的建议,我能够更新我的OnExit
方法来正确地跟踪异步执行而没有太多的开销。我认为问题的大部分实际上是使用AsyncBuilder
的相同实例,这导致了异步函数的额外调用。这是新的解决方案:
open System
open System.Diagnostics
open FSharp.Reflection
open MethodBoundaryAspect.Fody.Attributes
[<AllowNullLiteral>]
[<AttributeUsage(AttributeTargets.Method ||| AttributeTargets.Property, AllowMultiple = false)>]
type TraceAttribute () =
inherit OnMethodBoundaryAspect()
static let AsyncTypeDef = typedefof<Async<_>>
static let Tracer = typeof<TraceAttribute>
static let AsyncTracer = Tracer.GetMethod("TraceAsync")
let traceEvent (args: MethodExecutionArgs) (timestamp: int64) =
// Capture metrics here
()
member __.TraceAsync (asyncResult: Async<_>) trace =
async {
let! result = asyncResult
trace()
return result
}
override __.OnEntry (args) =
Stopwatch.GetTimestamp() |> traceEvent args
override __.OnExit (args) =
let exit () = Stopwatch.GetTimestamp() |> traceEvent args
match args.ReturnValue with
| :? System.Threading.Tasks.Task as task ->
task.ContinueWith(fun _ -> exit()) |> ignore
| other ->
let clrType = other.GetType()
if clrType.IsGenericType && clrType.GetGenericTypeDefinition() = AsyncTypeDef then
let generics = clrType.GetGenericArguments()
let result = AsyncTracer.MakeGenericMethod(generics).Invoke(this, [| other; exit |])
args.ReturnValue <- result
else
exit()
这似乎可以用更少的开销正确地跟踪Async函数。我确实想跟踪从调用函数起而不是从异步实际开始起的总时间,所以我将OnEntry
实现保留为相同。