0

我有一个 LoggerBase 类,它看起来像:

    public class BatchLoggerBase : IDisposable
    {
        protected string LogFilePath { private get; set; }
        protected object _synRoot;

        BatchLoggerBase(string logFilePath)
        {
            LogFilePath = logFilePath;
        }

        protected virtual void WriteToLog(string message)
        {
            Task.Factory.StartNew(() =>
            {
                lock (_synRoot)
                {
                    System.IO.File.AppendAllText(LogFilePath, message);
                }
            });
        }
        //Other code... 
    }

我有另一个类继承自这个基类,例如:

public sealed class TransactionBatchLogger : BatchLoggerBase
{
    public TransactionBatchLogger()
    {
         _synRoot = new object();
         string directory = AppDomain.CurrentDomain.BaseDirectory + ConfigurationManager.AppSettings["Batch.TransactionLog.Path"];
            if (!Directory.Exists(directory))
                Directory.CreateDirectory(directory);

            LogFilePath = string.Format("{0}{1}_{2}.txt", directory, "TransactionLog", DateTime.Now.ToString("yyyy-MM-dd"));
    }
public void LogLoyaltyPointProcess(IEnumerable<CustomerTierOverrideItem> listOfCustomerTierItem)
        {
            Task.Factory.StartNew(() =>
            {
                //Construct message...
                WriteToLog(message);
            });
        }
}

public sealed class LoyaltyPointBatchLogger : BatchLoggerBase
{
    public LoyaltyPointBatchLogger()
        {
            _synRoot = new object();

            string directory = AppDomain.CurrentDomain.BaseDirectory + ConfigurationManager.AppSettings["Batch.LoyaltyPointLog.Path"];
            if (!Directory.Exists(directory))
                Directory.CreateDirectory(directory);

            LogFilePath = string.Format("{0}{1}_{2}.txt", directory, "LoyaltyPointLog", DateTime.Now.ToString("yyyy-MM-dd"));
        }
public void LogLoyaltyPointProcess(IEnumerable<CustomerTierOverrideItem> listOfCustomerTierItem)
        {
            Task.Factory.StartNew(() =>
            {
                //Construct message...
                WriteToLog(message);
            });
        }
}

LoyaltyPointBatchLogger 和 TransactionBatchLogger 将日志内容写入不同的日志文件(一个用于 transactionLog,另一个用于 LoayltyPointLog),但它们都从基类调用相同的虚拟方法。

批处理程序逐批处理数据(例如,总数据45000个,每批10000个)这两个记录器可能会被不同的批次连续调用,所以我不希望日志文件被不同的批次记录器线程访问。

问题是:我应该在派生类 LoyaltyPointBatchLogger 和 TransactionBatchLogger 中还是在基类中实例化 _synRoot?

LoyaltyPointBatchLogger 和 TransactionBatchLogger 中实例化的 _synRoot 是不同的引用,所以 LoyaltyPointBatchLogger 和 TransactionBatchLogger 在进入 lock 语句时不会互相等待,对吧?

4

2 回答 2

2

实例化发生在基类中,syncRoot是一个实例字段⇒ 基类的每个实例或其任何后代都在每个实例的基础上同步并发访问。

实例化发生在基类中,syncRoot是一个静态字段⇒ 基类的所有实例或其任何后代在“单例”基础上同步并发访问。

实例化发生在派生类中,syncRoot在构造函数中实例化⇒基类的每个实例或其任何后代都在每个实例的基础上同步并发访问。

实例化发生在派生类中,syncRoot是在派生类中声明和实例化的静态字段⇒每个派生类的所有实例在每个派生类(类型)的基础上同步并发访问。

注意:最后一种情况对应于 Chris Sinclair 在他的回答中提出的建议。

于 2012-10-02T22:03:19.627 回答
1

我没有任何使用“syncroot”锁定模式的经验,但是如果我正确理解了您的问题,如果我会通过让每个子类类型按类型静态声明其自己唯一的 SyncRoot 来解决这个问题。这样,该类型的所有实例共享相同的 SyncRoot。此外,我会要求通过构造函数为基本记录器提供一个 SyncRoot 对象,而不是希望子类分配它(并及时分配它)。此外,我会使其不可变,因此子类不能做坏事。

批处理记录器库

public abstract class BatchLoggerBase
{
    protected readonly object SyncRoot;

    protected BatchLoggerBase(object syncRoot)
    {
        if (syncRoot == null)
            throw new ArgumentNullException("syncRoot");

        this.SyncRoot = syncRoot;
    }
}

LoyaltyPointBatchLogger

public class LoyaltyPointBatchLogger : BatchLoggerBase
{
    private static readonly object LOYALTY_SYNC_ROOT = new object();

    public LoyaltyPointBatchLogger()
        : base(LOYALTY_SYNC_ROOT)
    {

    }
}

TransactionBatchLogger

public class TransactionBatchLogger : BatchLoggerBase
{
    private static readonly object TRANSACTION_SYNC_ROOT = new object();

    public TransactionBatchLogger()
        : base(TRANSACTION_SYNC_ROOT)
    {

    }
}

编辑:请注意,这仍然意味着子类可以忽略您的意图。例如:

public class EvilBatchLogger : BatchLoggerBase
{
    public EvilBatchLogger()
        : base(new object())
    {

    }
}

var evil1 = new EvilBatchLogger();
var evil2 = new EvilBatchLogger();

在这种情况下,并不会evil共享evil2同一个锁定对象并且可以干扰。但是,如果您可以控制记录器的实现,则可以避免自取其辱。

于 2012-10-02T21:51:02.633 回答