将在派生类中实例化的对象传递给lock语句

时间:2012-10-02 21:38:07

标签: c# locking

我有一个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语句时不会互相等待,对吗?

2 个答案:

答案 0 :(得分:2)

实例化发生在基类中,syncRoot实例字段⇒基类的每个实例或其任何后代在每个实例的基础上同步并发访问< / em>的

实例化发生在基类中,syncRoot静态字段⇒基类的所有实例或其任何后代在'singleton'的基础上同步并发访问< / em>的

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

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

注意:最后一个案例与Chris Sinclair在答案中的建议相对应。

答案 1 :(得分:1)

我还没有使用&#34; syncroot&#34;锁定模式,但如果我正确理解你的问题,如果我通过让每个子类类型按类型静态声明它自己的唯一SyncRoot来攻击它。这样,该类型的所有实例共享相同的SyncRoot。另外,我要求通过构造函数向基本记录器提供SyncRoot对象,而不是希望子类分配它(并及时分配)。另外,我会让它变成不可变的,所以子类不能做坏事。

<强> BatchLoggerBase

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();

在这种情况下,evilevil2 共享相同的锁定对象并可能会发生干扰。但是如果你能控制记录器的实现,就可以避免自己陷入困境。