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 はログ コンテンツを異なるログ ファイル (1 つは transactionLog 用、もう 1 つは LoayltyPointLog 用) に書き込みますが、それらはすべて基本クラスから同じ仮想メソッドを呼び出します。

バッチ プログラムはバッチごとにデータ バッチを処理します (合計 45000 データと各バッチ 10000 のように) これら 2 つのロガーは異なるバッチによって連続して呼び出される可能性があるため、ログ ファイルが異なるバッチ ロガー スレッドによってアクセスされることは望ましくありません。

問題は、_synRoot を派生クラス LoyaltyPointBatchLogger と TransactionBatchLogger、または基本クラスでインスタンス化する必要があるかどうかです。

LoyaltyPointBatchLogger と TransactionBatchLogger でインスタンス化された _synRoot は異なる参照であるため、LoyaltyPointBatchLogger と TransactionBatchLogger は、lock ステートメントに入ったときに互いに待機しませんよね?

4

2 に答える 2

2

インスタンス化は基本クラスで発生します。これsyncRootインスタンスフィールドです。⇒基本クラスの各インスタンスまたはその子孫は、インスタンスごとに同時アクセスを同期します。

インスタンス化は基本クラスで発生しsyncRoot静的フィールドです⇒基本クラスのすべてのインスタンスまたはその子孫のいずれかが「シングルトン」ベースで同時アクセスを同期します。

インスタンス化は派生クラスで発生しsyncRoot、コンストラクターでインスタンス化されます⇒基本クラスの各インスタンスまたはその子孫は、インスタンスごとに同時アクセスを同期します。

インスタンス化は派生クラスで発生し、派生クラスで宣言およびインスタンス化さsyncRootれる静的フィールドです。⇒各派生クラスのすべてのインスタンスは、派生クラス(タイプ)ごとに同時アクセスを同期します。

注:最後のケースは、ChrisSinclairが彼の回答で提案したものに対応しています。

于 2012-10-02T22:03:19.627 に答える
1

「syncroot」ロックパターンを使用した経験はありませんが、質問を正しく理解していれば、各サブクラスタイプに独自の一意の 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は同じロック オブジェクトを共有せず、干渉する可能性があります。ただし、ロガーの実装を制御できる場合は、自分自身を撃つことを避けることができます。

于 2012-10-02T21:51:02.633 に答える