2015-04-22 24 views
5

Zmagam się z problemem, który ostatnio pojawił się w prostej klasie logtofile, którą napisałem.Zmiana ciągu znaków na const łamie moją klasę rejestratora

using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Threading; 

namespace Assets.Code 
{ 
    class TimingLogger 
    { 
     public static readonly TimingLogger Logger = new TimingLogger(); 
     private static readonly string path = "C:\\Logs\\TimingLog.txt"; 
     private readonly Mutex mutex = new Mutex(false, path); 
     private StreamWriter writer; 
     private readonly Queue<string> queue = new Queue<string>(); 
     private bool isRunning; 
     private readonly object obj = new object(); 

     private TimingLogger() 
     { 

     } 

     public void CheckPath() 
     { 
      if (!File.Exists(path)) 
      { 
       File.Create(path); 
      } 
     } 

     public void Run() 
     { 
      isRunning = true; 
      while (isRunning) 
      { 
       lock (obj) 
       { 
        while (queue.Count <= 0) 
        { 
         Monitor.Wait(obj); 
        } 
        Log(queue.Dequeue()); 
       } 
      } 
     } 

     public void Log(string line) 
     { 
      try 
      { 
       mutex.WaitOne(); 
       writer = File.AppendText(path); 
       writer.WriteLine(line); 
       writer.Close(); 
      } 
      catch (Exception) 
      { 
       //throw; 
      } 
      finally 
      { 
       mutex.ReleaseMutex(); 
      } 
     } 

     public void Enqueue(string line) 
     { 
      lock (obj) 
      { 
       queue.Enqueue(line); 
       Monitor.Pulse(obj); 
      } 
     } 

     public void Stop() 
     { 
      isRunning = false; 
     } 
    } 
} 

Ta klasa działała dobrze do niedawna, gdy zauważyłem, że mój plik dziennika nie zawierał danych, które oczekiwałem. Co dziwne, nie zmieniłem żadnej funkcjonalności klasy. Porównując starą, działającą wersję z moją nową, jedyną różnicą było to, że niektóre z moich pól zostały utworzone private i readonly. Poza tym string path został zmieniony na const. I do mojego kompletnego oszołomienia, zmiana tego z powrotem na readonly naprawiła problem, który miałem.

Moje pytanie brzmi: jak to możliwe? O ile mi wiadomo, nie powinno być funkcjonalnie żadnej różnicy między readonly i const w tej sytuacji.

Podczas debugowania zmiana w zachowaniu jest znaczna, szczególnie w metodzie Run(). To, co się stanie, to: po wywołaniu Log(queue.Dequeue());, wątek opuści instrukcję lock i ponownie przejdzie przez pętlę while (isRunning). Wydaje się to dość oczywiste, prawda? Jednak po zmianie string path na const i ponownym debugowaniu, Log(queue.Dequeue()); jest przekazywany raz, a pojedyncza instrukcja znajduje się w pliku dziennika, po czym po prostu nie robi nic więcej. Nie przechodzi ponownie obok while (isRunning) i wydaje się, że nie opuszcza bloku lock (obj). Wątek programu rejestrującego wydaje się po prostu wyłączać lub wstrzymywać po pomyślnym wywołaniu.

Faktycznie odrzucenie wyjątku w metodzie Log nie ma znaczenia, nie są zgłaszane żadne wyjątki, ponieważ samo rejestrowanie działa poprawnie.

Należy wspomnieć, że używam tego kodu z Unity3D 5, który używa Mono. Wciąż jednak ta drastyczna zmiana w zachowaniu przez tak małą edycję wydaje mi się niemożliwa. Czy ktoś może wyjaśnić, dlaczego tak się dzieje?

Dzięki!

+0

myślałem, że to trochę za dużo kod aby umieścić wewnątrz treści wiadomości, ale w porządku, Dodałem to. – David

+0

To znacznie lepiej niż link, który może zniknąć i sprawić, że pytanie będzie niezrozumiałe w późniejszym czasie. Pisanie rejestratora to trudna sprawa, która została już rozwiązana. Czy nie byłoby lepiej użyć istniejącego rozwiązania, takiego jak [nLog] (http://nlog-project.org/)? – spender

+0

Zabawne, że powinieneś o tym wspomnieć, spróbowałem nLog, ale napotkałem pewne problemy z uruchomieniem go z Unity3D 5. Po prostu powtórzę, mój logger działa dobrze teraz, gdy już zlokalizowałem i naprawiłem problem. Po prostu nie rozumiem problemu. – David

Odpowiedz

1

Oto różnica:

Consts są tworzone w metadanych plików, więc po uruchomieniu ci klasy wartość już istnieje.

ReadOnly są inicjowane w czasie kompilacji, w danym przypadku, heres trick, nawet jeśli zadeklarowana ścieżkę pierwszy następnie mutex, kompilator zainicjowany pierwszy obiekt mutex, oto dlaczego:

Twój pierwszy statyczny obiekt do być inicjowane jest Rejestrator:

public static readonly TimingLogger Logger = new TimingLogger(); 

Bo nazywany konstruktorem, niebędące członkami statyczne są inicjowane, co mutex następnego uczestnika zostać zainicjowany. W tym momencie nie zainicjalizowano jeszcze ścieżki, więc tworzysz obiekt mutex z parametrami false i null.

Jeśli chcesz mieć ten sam błąd, że masz z const używając tylko do odczytu, można wymusić kolejność statycznej parametrów inicjalizacji przy użyciu konstruktora statycznego jak:

static TimingLogger() 
{ 
    path = "C:\\Logs\\TimingLog.txt"; 
    Logger = new TimingLogger(); 
} 

lub po prostu oddanie ścieżkę przed Logger.

Jeśli nie chcesz mieć błąd za pomocą const, wystarczy zmienić inicjalizacji mutex stosując zerową parametr:

private readonly Mutex mutex = new Mutex(false, null);