我有一个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语句时不会互相等待,对吗?
答案 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();
在这种情况下,evil
和evil2
将 不 共享相同的锁定对象并可能会发生干扰。但是如果你能控制记录器的实现,就可以避免自己陷入困境。