使用带有异步任务的语句不在等待

时间:2016-03-03 04:29:07

标签: c# asynchronous async-await using-statement

  1. 我创建了一个IDisposable对象,用于在Dispose上记录执行时间。

  2. 然后,我创建了一个RealProxy,它使用此对象记录我的业务类的任何方法调用的时间

  3. 我的业务类使用异步方法,然后在完成任务之前保留using语句,然后记录错误的执行时间。

  4. 这是我的代码:

    /// <summary>
    ///  Logging proxy with stopwatch
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public sealed class LoggingProxy<T> : RealProxy// where T : MarshalByRefObject
    {
        private ILog _logger;
        private readonly T _instance;
    
        private LoggingProxy(T instance, ILog logger)
            : base(typeof(T))
        {
            _logger = logger;
            _instance = instance;
        }
    
        /// <summary>
        /// Create the Transparent proy for T
        /// </summary>
        /// <param name="type">An implementation type of T</param>
        /// <returns>T instance</returns>
        public static T Create(ILog logger)
        {
                logger.DebugFormat("[{0}] Instantiate {1}", "LoggingProxy", typeof(T).Name);
                var instance = (T)Activator.CreateInstance(typeof(T), logger);
    
                //return the proxy with execution timing if debug enable in logger
                if (logger.IsDebugEnabled)
                    return (T)new LoggingProxy<T>(instance, logger).GetTransparentProxy();
                else
                    return instance;
        }
    
    
        /// <summary>
        /// Invoke the logging method using Stopwatch to log execution time
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        public override IMessage Invoke(IMessage msg)
        {
            var methodCall = (IMethodCallMessage)msg;
            var method = (MethodInfo)methodCall.MethodBase;
    
            string methodName = method.Name;
            string className = method.DeclaringType.Name;
    
            //return directly methods inherited from Object
            if (method.DeclaringType.Name.Equals("Object"))
            {
                var result = method.Invoke(_instance, methodCall.Args);
                return new ReturnMessage(result, null, 0, methodCall.LogicalCallContext, methodCall);
            }
    
            using (var logContext = _logger.DebugTiming("[{0}] Execution time for {1}", className, methodName))
            {
                _logger.DebugFormat("[{0}] Call method {1}", className, methodName);
                //execute the method
                //var result = method.Invoke(_instance, methodCall.Args);
                object[] arg = methodCall.Args.Clone() as object[];
                var result = method.Invoke(_instance, arg);
    
                //wait the task ends before log the timing
                if (result is Task)
                    (result as Task).Wait();
    
                return new ReturnMessage(result, arg, 0, methodCall.LogicalCallContext, methodCall);
            }
    
        }
    

    _logger.DebugTiming方法启动一个秒表并将其记录在Dispose上。  我发现使用异步方法的唯一方法是使用该行:

                //wait the task ends before log the timing
                if (result is Task)
                    (result as Task).Wait();
    

    但我感觉我通过这样做打破了异步方法的所有好处。

    - &GT;如果您有关于如何正确实施的建议

    - &GT;想知道在代理上调用wait()的真正影响吗?

1 个答案:

答案 0 :(得分:2)

您可以使用Task.ContinueWith():

public override IMessage Invoke(IMessage msg)
{
    var methodCall = (IMethodCallMessage)msg;
    var method = (MethodInfo)methodCall.MethodBase;

    string methodName = method.Name;
    string className = method.DeclaringType.Name;

    //return directly methods inherited from Object
    if (method.DeclaringType.Name.Equals("Object"))
    {
        var result = method.Invoke(_instance, methodCall.Args);
        return new ReturnMessage(result, null, 0, methodCall.LogicalCallContext, methodCall);
    }

    var logContext = _logger.DebugTiming("[{0}] Execution time for {1}", className, methodName);
    bool disposeLogContext = true;
    try
    {
        _logger.DebugFormat("[{0}] Call method {1}", className, methodName);
        //execute the method
        //var result = method.Invoke(_instance, methodCall.Args);
        object[] arg = methodCall.Args.Clone() as object[];
        var result = method.Invoke(_instance, arg);

        //wait the task ends before log the timing
        if (result is Task) {
            disposeLogContext = false;
            ((Task)result).ContinueWith(() => logContext.Dispose());
        }

        return new ReturnMessage(result, arg, 0, methodCall.LogicalCallContext, methodCall);
    }
    finally
    {
        if (disposeLogContext)
            logContext.Dispose();
    }
}

不要在任务上调用Wait() - 这会改变行为并可能导致死锁。