2010-06-02 23 views
9

Basit, çoklu güvenlikli bir günlük kaydı oluşturmanın en iyi yolu nedir? Böyle bir şey yeterli mi? İlk oluşturulduğunda günlüğü nasıl temizlerim?Simple MultiThread Güvenli Günlük Sınıfı

public class Logging 
{ 
    private static readonly object locker = new object(); 

    public Logging() 
    { 
    } 

    public void WriteToLog(string message) 
    { 
     lock(locker) 
     { 
      StreamWriter SW; 
      SW=File.AppendText("Data\\Log.txt"); 
      SW.WriteLine(message); 
      SW.Close(); 

      SW.Dispose(); 
     } 
    } 
} 

static olarak günlük sınıf bildirmek için iyi olabilir ve @Adam Robinson olarak kilitleme nesnesi önerdi:

public class Logging 
{ 
    public Logging() 
    { 
    } 

    public void WriteToLog(string message) 
    { 
     object locker = new object(); 

     lock(locker) 
     { 
      StreamWriter SW; 
      SW=File.AppendText("Data\\Log.txt"); 
      SW.WriteLine(message); 
      SW.Close(); 
     } 
    } 
} 

public partial class MainWindow : Window 
{ 
    public static MainWindow Instance { get; private set; } 
    public Logging Log { get; set; } 

    public MainWindow() 
    { 
     Instance = this; 
     Log = new Logging(); 
    } 
} 
+0

Kendi günlük uygulamanızı yazmak yerine, [log4net] (http://logging.apache.org/log4net/index.html) ve diğerlerine bir göz attığınızdan emin olun. Bu sadece bir Windows uygulaması ve paralel performans bir endişe ise, [NTrace] (http://ntrace.codeplex.com/) da düşünün. – hemp

cevap

11

Hayır, yöntem her çağrıldığında yeni bir kilit nesnesi oluşturuyorsunuz. Bir kerede yalnızca bir iş parçacığının bu işlevdeki kodu yürütebilmesini sağlamak istiyorsanız, locker işlevini bir örnek veya statik bir üyeye taşıyın. Bu sınıf bir giriş yazılacak her seferinde başlatılırsa, locker muhtemelen statik olmalıdır. Tek bir monitör (kilidi) kullanarak bir iş parçacığı güvenli günlüğü uygulamasını oluşturma

public class Logging 
{ 
    public Logging() 
    { 
    } 

    private static readonly object locker = new object(); 

    public void WriteToLog(string message) 
    { 
     lock(locker) 
     { 
      StreamWriter SW; 
      SW=File.AppendText("Data\\Log.txt"); 
      SW.WriteLine(message); 
      SW.Close(); 
     } 
    } 
} 
+0

Kayıt sınıfını nasıl kullanmak istediğimi göstermek için kodumu güncelledim. – Robert

+0

@Robert: O zaman kodu yazdığım gibi almalısın (statik kilitleme değişkenine sahip statik olmayan bir sınıf). Başka bir yerde bulunan "Logging" sınıfının birden fazla örneğine sahip olabilirsiniz, ancak yine de sadece bir defada dosyaya yazabilmenizi istersiniz. –

5

sınıf düzeyinde eşitleme nesnesi ilan etmek gerekir.

+0

Kimden önce en yeni soruyu görüyor musunuz? lol .. – VoodooChild

+0

Günlüğü yapıcıda nasıl temizlersiniz? – Robert

+0

@hemp: OP'nin sorusuna yorum göndermenizi öneririz ... –

7

olumlu sonuçlar vermeye olası değildir. Bunu doğru bir şekilde yapabilmenize rağmen ve nasıl yapılacağını gösteren birkaç cevap gönderilmiştir, çünkü günlük kaydı yapan her nesnenin günlük kaydı yapan diğer nesnelerle senkronize olması gerektiğinden, performans üzerinde dramatik bir olumsuz etki yaratacaktır. Bunu yaparken aynı anda birden fazla ya da iki iş parçacığı edinin ve aniden işlenmekten daha fazla zaman harcayabilirsiniz.

Tek monitör yaklaşımıyla karşılaştığınız diğer bir sorun da, iş parçacığının kilitlenmeyi, başlangıçta istedikleri sırada almasını garanti etmemenizdir. Bu nedenle, günlük girdileri esasen sıra dışı görünebilir. İzleme günlüğü için kullanıyorsanız, bu sinir bozucu olabilir.

Çok iş parçacığı zor. Hafifçe yaklaşmak her zaman hatalara yol açacaktır. günlükçüsüne arayanlar ancak bu şekilde büyük ölçüde performans kaybına azaltarak diske yazmak için bir ara bellek yazma ve dönüş hemen yerine kaydedici beklemek gerekir, burada bu soruna

bir yaklaşım Producer/Consumer pattern, uygulamak olacaktır . Günlük çerçevesi, ayrı bir iş parçacığı üzerinde, günlük verilerini tüketir ve bunu sürdürür.

18

Burada, BlockingCollection kullanılarak Üretici/Tüketici modeli ile (.Net 4 ile) uygulanan bir Günlük örneğidir.

namespace Log 
{ 
    public interface ILogger 
    { 
     void WriteLine(string msg); 
     void WriteError(string errorMsg); 
     void WriteError(string errorObject, string errorAction, string errorMsg); 
     void WriteWarning(string errorObject, string errorAction, string errorMsg); 
    } 
} 

ve tam sınıf kodu burada: arayüzüdür

Bu örnekte
using System; 
using System.Collections.Concurrent; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 

namespace Log 
{ 
    // Reentrant Logger written with Producer/Consumer pattern. 
    // It creates a thread that receives write commands through a Queue (a BlockingCollection). 
    // The user of this log has just to call Logger.WriteLine() and the log is transparently written asynchronously. 

    public class Logger : ILogger 
    { 
     BlockingCollection<Param> bc = new BlockingCollection<Param>(); 

     // Constructor create the thread that wait for work on .GetConsumingEnumerable() 
     public Logger() 
     { 
      Task.Factory.StartNew(() => 
        { 
         foreach (Param p in bc.GetConsumingEnumerable()) 
         { 
          switch (p.Ltype) 
          { 
           case Log.Param.LogType.Info: 
            const string LINE_MSG = "[{0}] {1}"; 
            Console.WriteLine(String.Format(LINE_MSG, LogTimeStamp(), p.Msg)); 
            break; 
           case Log.Param.LogType.Warning: 
            const string WARNING_MSG = "[{3}] * Warning {0} (Action {1} on {2})"; 
            Console.WriteLine(String.Format(WARNING_MSG, p.Msg, p.Action, p.Obj, LogTimeStamp())); 
            break; 
           case Log.Param.LogType.Error: 
            const string ERROR_MSG = "[{3}] *** Error {0} (Action {1} on {2})"; 
            Console.WriteLine(String.Format(ERROR_MSG, p.Msg, p.Action, p.Obj, LogTimeStamp())); 
            break; 
           case Log.Param.LogType.SimpleError: 
            const string ERROR_MSG_SIMPLE = "[{0}] *** Error {1}"; 
            Console.WriteLine(String.Format(ERROR_MSG_SIMPLE, LogTimeStamp(), p.Msg)); 
            break; 
           default: 
            Console.WriteLine(String.Format(LINE_MSG, LogTimeStamp(), p.Msg)); 
            break; 
          } 
         } 
        }); 
     } 

     ~Logger() 
     { 
      // Free the writing thread 
      bc.CompleteAdding(); 
     } 

     // Just call this method to log something (it will return quickly because it just queue the work with bc.Add(p)) 
     public void WriteLine(string msg) 
     { 
      Param p = new Param(Log.Param.LogType.Info, msg); 
      bc.Add(p); 
     } 

     public void WriteError(string errorMsg) 
     { 
      Param p = new Param(Log.Param.LogType.SimpleError, errorMsg); 
      bc.Add(p); 
     } 

     public void WriteError(string errorObject, string errorAction, string errorMsg) 
     { 
      Param p = new Param(Log.Param.LogType.Error, errorMsg, errorAction, errorObject); 
      bc.Add(p); 
     } 

     public void WriteWarning(string errorObject, string errorAction, string errorMsg) 
     { 
      Param p = new Param(Log.Param.LogType.Warning, errorMsg, errorAction, errorObject); 
      bc.Add(p); 
     } 

     string LogTimeStamp() 
     { 
      DateTime now = DateTime.Now; 
      return now.ToShortTimeString(); 
     } 

    } 
} 

, BlockingCollection yoluyla yazma iplik bilgi aktarmak için kullanılan iç Param sınıftır:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace Log 
{ 
    internal class Param 
    { 
     internal enum LogType { Info, Warning, Error, SimpleError }; 

     internal LogType Ltype { get; set; } // Type of log 
     internal string Msg { get; set; }  // Message 
     internal string Action { get; set; } // Action when error or warning occurs (optional) 
     internal string Obj { get; set; }  // Object that was processed whend error or warning occurs (optional) 

     internal Param() 
     { 
      Ltype = LogType.Info; 
      Msg = ""; 
     } 
     internal Param(LogType logType, string logMsg) 
     { 
      Ltype = logType; 
      Msg = logMsg; 
     } 
     internal Param(LogType logType, string logMsg, string logAction, string logObj) 
     { 
      Ltype = logType; 
      Msg = logMsg; 
      Action = logAction; 
      Obj = logObj; 
     } 
    } 
} 
+1

Çok iş parçacıklı sorunların nasıl çözüleceğine çok güzel bir örnek. Bu daha fazla oylanmalıdır. – MadTigger

İlgili konular