オプション2が最適です。オプション1はあまりにも多くのバックグラウンドスレッドを作成し、オプション3は必要以上に複雑に聞こえるでしょう。
このようなものを試してみるとよいでしょう。
このクラスを考える:ロギングを実行する
class LogEntry
{
public string IpAddress { get; set; }
public string UserAgent { get; set; }
public DateTime TimeStamp { get; set; }
public string Url { get; set; }
//whatever else you need
}
使用このクラス:
class SiteLogger
{
private static object loggerLock = new object();
private static List<LogEntry> _pendingEntries = new List<LogEntry>();
private static Thread savingThread;
public static void AddEntry(LogEntry entry)
{
// lock when accessing the list to avoid threading issues
lock (loggerLock)
{
_pendingEntries.Add(entry);
}
if (savingThread == null)
{
// this should only happen with the first entry
savingThread = new Thread(SaveEntries);
savingThread.Start();
}
}
private static void SaveEntries()
{
while (true)
{
while (_pendingEntries.Count > 0)
{
// lock around each individual save, not the whole loop
// so we don't force one web request to wait for
// all pending entries to be saved.
lock (loggerLock)
{
// save an entry to the database, however the app does that
MyDatabase.SaveLogEntry(_pendingEntries[0]);
_pendingEntries.RemoveAt(0);
}
}
Thread.Sleep(TimeSpan.FromSeconds(2));
// 2 seconds is a bit of an arbitrary value. Depending on traffic levels,
// it might need to go up or down.
}
}
}
私は、任意のデータベースの関与なしに、簡単なコマンドラインテストアプリでこれを実行した(して、データベースの呼び出しをシミュレート10ミリ秒間スリープ状態になりました)、それはうまくいくように見えましたが、実際に運用環境に入る前に、より多くのテストを行う必要があります。また、データベースに保存するよりも要求が早くなった場合にも問題は発生します(これはほとんどありませんが、考慮する必要があります)。
アップデート、2018年2月:は今、このを見て、私はあなたが(あなたが意志と仮定しなければならない)スレッドタイミングに不運取得する場合、あなたは2つのsavingThread
のインスタンスで終わることができることを実現しています。そして、new Thread()
は、C#でこのようなことをする古い方法のようなものです。私は現代的でスレッドセーフな実装を読者に任せています。これがあることを確認するなど、依存性注入、IoCの、のように、
class SiteLogger
{
private readonly Lazy<BlockingCollection<LogEntry>> _messageQueue = new Lazy<BlockingCollection<LogEntry>>(() =>
{
var collection = new BlockingCollection<LogEntry>();
Task.Factory.StartNew(processMessages, TaskCreationOptions.LongRunning);
return collection;
void processMessages()
{
foreach (var entry in collection.GetConsumingEnumerable())
{
//Do whatever you need to do with the entry
}
}
}, LazyThreadSafetyMode.ExecutionAndPublication);
public void AddEntry(LogEntry logEntry) => _messageQueue.Value.TryAdd(logEntry);
}
その他のベスト・プラクティス: