多线程使用的Post Sharp属性中的唯一记录器

时间:2016-09-02 08:49:37

标签: c# multithreading task-parallel-library postsharp

我已经实现了Postsharp属性,如下所示,以下是用法详情:

  • static Func<ILog> GetLoggerstatic ThreadLocal<ILog> TestLogger,可以从主程序
  • 的方法HelloTask设置
  • 目标是为调用方法DoTask
  • 的每个线程分别设置一个记录器

我非常谨慎地设置static Funcstatic ThreadLocal,并认为这会因多线程导致问题(竞争条件或损坏),但可以看出在Main方法中,即使调用了100个任务,它也能完美地运行,所以这个方法是正确的,哪个更好,我认为ThreadLocal会更好

有没有更好的方法来实现相同目标?

    [Serializable]
    public class LogExecutionTimeAttribute : OnMethodBoundaryAspect
    {

    [NonSerialized]
    public static Func<ILog> GetLogger;

    [NonSerialized]
    public static ThreadLocal<ILog> TestLogger;

    public LogExecutionTimeAttribute()
    {
        // Setting AspectPriority explicity avoids undeterministic behaviour 
        // when multiple aspects are applied, and avoids warning messages
        this.AspectPriority = 1;
    }       

    public override void OnEntry(MethodExecutionArgs args)
    {
        args.MethodExecutionTag = Stopwatch.StartNew();
        base.OnEntry(args);
    }

    public override void OnExit(MethodExecutionArgs args)
    {
        Stopwatch sw = (Stopwatch)args.MethodExecutionTag;
        sw.Stop();
        var logger =
     #if Func
           GetLogger();
     #else
        TestLogger.Value;
     #endif 

        logger.DebugFormat(
            "{0}.{1} for Id={2} executed in {3} seconds.", 
            this.className, 
            this.methodName, 
            args.Arguments[0],
            sw.ElapsedMilliseconds / 1000.0);
        base.OnExit(args);
      }
     }

以下是用法:

 class Program
    {

   static void Main(string[] args)
        {
            var taskList = new List<Task>();

            for (var counter = 0; counter < 100; counter++)
            {
                var localCounter = counter;

                taskList.Add(Task.Factory.StartNew(() => HelloTask(localCounter + 1), TaskCreationOptions.LongRunning));
            }

            Task.WaitAll(taskList.ToArray());
        }

        [LogExecutionTime]
        private static void DoTask(int id)
        {
 #if Func
            LogExecutionTimeAttribute.GetLogger().Info(id);
#else
                LogExecutionTimeAttribute.TestLogger.Value.Info(id);
#endif
        }


        private static void HelloTask(int id)
        {
            var log  = new LogFileLogger(id.ToString()).LogInstance;

            #if Func
                LogExecutionTimeAttribute.GetLogger = () => log;
            #else
                LogExecutionTimeAttribute.TestLogger = new ThreadLocal<ILog>(() => log);
            #endif

            var sw = Stopwatch.StartNew();

            for (var i = 0; i < 50; i++)
            {
                DoTask(i);
            }

            sw.Stop();

        #if Func
            LogExecutionTimeAttribute.GetLogger().Info("Time :: " + sw.ElapsedMilliseconds);
        #else
            LogExecutionTimeAttribute.TestLogger.Value.Info("Time :: " + sw.ElapsedMilliseconds);
        #endif

        }    

    }

1 个答案:

答案 0 :(得分:2)

如对the similar question on Code Review site的响应中所述:当每个线程都有不同的记录器实例时,应将其存储在线程静态或线程本地字段中,以避免并发问题。

从设计的角度来看,最好还要使方面与创建记录器实例的方式无关。因此,例如,在应用启动时仅为静态GetLogger分配一次,并将其留给方面使用者,以决定是否应为每个线程,每个实例或每个应用创建记录器。

[Serializable]
public class LogExecutionTimeAttribute : OnMethodBoundaryAspect
{
    public static Func<ILog> GetLogger;
    // ...
}

public static class LoggerFactory
{
    [ThreadStatic]
    public static ILog Logger;

    public static ILog GetLogger()
    {
        return Logger;
    }
}

class Program
{
    static void Main(string[] args)
    {
        // Configure factory only once.
        LogExecutionTimeAttribute.GetLogger = LoggerFactory.GetLogger;
        // ...
    }

    private static void HelloTask(int id)
    {
        var log  = new LogFileLogger(id.ToString()).LogInstance;
        LoggerFactory.Logger = log;
        // ...
     }
}

您可以从本文档页面的各个方面阅读有关使用依赖项的更多信息:http://doc.postsharp.net/consuming-dependencies