2011-03-29 2 views
0

이것은 멀티 스레딩에 대한 나의 첫 번째 모험이며 몇 가지 주요 개념을 놓치고 있다고 생각합니다. 그래서 어떤 도움을 주시면 감사하겠습니다. asp.net 응용 프로그램에 대한 로그 관리자를 만들려고합니다. 우리 시스템에서 많은 양의 데이터에 대해 조회/삽입/수정/삭제를 로깅합니다. 끊임없이 행을 삽입하는 대신, 메모리에 로그 항목 목록을 보유하는 싱글 톤을 생성하여 특정 크기에 도달 할 때까지 한 번에 모두 db에 기록하는 방법이라고 생각했습니다. 그런 다음 로그를 작성할 필요가있을 때 새 스레드에서이를 실행하면 성능이 향상 될 것이라고 생각했습니다. 아래는 나의 테스트 코드이다. 스레딩을 제거하면 DB에서 500 행을 얻을 수 있지만 멀티 스레딩을 사용하면 약 200-300이됩니다. 약 절반의 레코드가 삽입되지 않습니다. 이것이 멀티 스레딩에 유효한 용도인가요? 제가 뭘 잘못하고 있습니까? 고맙습니다.이 여러 가지 예에서 내가 뭘 잘못하고 있습니까?

LogManager의 :

public sealed class LogManager 
    { 
    private static LogManager _Log = null; 
    private static readonly object singletonLock = new object(); 
    private static readonly object listLock = new object(); 

    private List<LogEntry> LogEntries { get; set; } 

    public static LogManager Log 
    { 
     get 
     { 
     if (_Log == null) 
     { 
      lock (singletonLock) 
      { 
      if (_Log == null) 
      { 
       _Log = new LogManager(); 
      } 
      } 
     } 
     return _Log; 
     } 
    } 

    public LogManager() 
    { 
     LogEntries = new List<LogEntry>(); 
    } 

    public void Add(LogEntry logEntry) 
    { 
     lock (listLock) 
     { 
     LogEntries.Add(logEntry); 
     if (LogEntries.Count >= 100) 
     {   
      ThreadStart thread = delegate { Flush(new List<LogEntry>(LogEntries)); }; 
      new Thread(thread).Start(); 
      //Flush(LogEntries);   
      LogEntries.Clear(); 
     } 
     } 
    } 

    private static void Flush(List<LogEntry> logEntries) 
    { 
     using (var conn = new SqlConnection(DAL.ConnectionString)) 
     { 
     using (var cmd = conn.CreateCommand()) 
     { 
      cmd.CommandType = CommandType.StoredProcedure; 
      cmd.CommandText = "spInsertLog"; 
      conn.Open(); 
      foreach (var logEntry in logEntries) 
      { 
      cmd.Parameters.AddWithValue("@ID", logEntry.ID); 
      try 
      { 
       cmd.ExecuteNonQuery(); 
      } 
      catch (Exception ex) { throw (ex);/*KeepGoing*/} 
      cmd.Parameters.Clear(); 
      } 
     }  
     } 
    } 
    } 

콘솔 응용 프로그램 : 나는 당신의 단 한 줄의 코드를 분석하기 전에

class Program 
    { 
    static void Main(string[] args) 
    { 
     var stopwatch = new Stopwatch();  
     for (int i = 0; i < 500; i++) 
     { 
     stopwatch.Start(); 
     LogManager.Log.Add(new LogEntry() { ID = i }); 
     Console.WriteLine(String.Format("Count: {0} Time: {1}",i.ToString(),stopwatch.ElapsedMilliseconds)); 
     stopwatch.Stop(); 
     stopwatch.Reset(); 
     }  
    } 
    } 
+0

내 첫 번째 추측은 루프가 중지되었을 때 아직 플러시되지 않은 로그 항목이 많다는 것입니다. – RQDQ

+0

'{throw (ex);/* KeepGoing * /}. '을 사용하지 마십시오. 그것은 스택을 엉망으로 만들고 여전히 예외를 처리해야합니다. –

+0

실수로 던져 버렸습니다. 딸꾹질을 당하면 계속 가고 싶었어요. 너는 내가 모두 잡아 먹는 것을 제거해야한다고 말하는거야? – Mike

답변

1

내가 볼 몇 가지를 수행하여이 문제를 해결할 수 있습니다 이 상황. 둘째, 스레드를 종료 한 후 바로 목록을 지 웁니다. 따라서 스레드가 실제로 코드를 실행하기 시작할 때까지는 목록이 이미 비어있을 가능성이 있습니다. Queue<>은 큐에있는 항목을 데이터베이스에 쓸 때 제거 할 수 있기 때문에이 문제를 해결하는 데 도움이됩니다.

또한 목록에 액세스하는 동안 코드를 잠그는 것이 좋습니다. 반복하는 동안 항목이 목록에 추가되면 예외가 발생할 수 있습니다. (당신이해야하는)

LogEntry myEntry; 
lock(sync) { 
    myEntry = myQueue.Dequeue(); 
} 

그리고 또한 Add 방법으로 잠금 : 이것은뿐만 아니라 Queue<>에 적용되는 것, 내가 일반적으로 무엇을 같은입니다.

1

, 로그 메시지에 대한 중간 저장이 잘못된 위치에 있습니다. 처리를 위해 LogManager를 기다리는 동안 MSMQ 또는 다른 대기열 메커니즘을 사용하여 메시지를 저장하는 것이 좋습니다.

다른 스레드에서 플러시를 호출하고 로그 항목 목록에 대한 참조를 전달한 다음 현재 스레드에서 목록을 지우고 있습니다. 새 스레드가 기록해야하는 항목 목록을 효과적으로 파기했습니다. LogEntries 필드를 지우기 전에 플러시 스레드에 LogEntries 목록의 복사본을 전달해야합니다.

아마 같은 것을 : ToList()이 당신의 세척 방법에 대한 목록의 복사본을 생성합니다

{Flush(LogEntries.ToList())} 

LINQ 식입니다.

제쳐두고, Flush 메서드를 IEnumerable<LogEntry>으로 변경하여 목록뿐만 아니라 다른 컬렉션도 메서드에 전달할 수 있습니다.

3
ThreadStart thread = delegate { Flush(new List<LogEntry>(LogEntries)); }; 
     new Thread(thread).Start(); 
     //Flush(LogEntries);   
     LogEntries.Clear(); 

List<LogEntry>은 참조 유형입니다. 새 스레드가 삽입하기 시작하지만 완료되기 전에 목록을 지우십시오. 멀티 스레딩을 사용하지 않으면 전체 목록을 지우기 전에 플러시 될 때까지 기다립니다. 그것은 더 적합이다, 첫째,이 List<>를 사용하지 않을 나는 Queue<>을 사용 : 당신은 배열을 취할 Flush의 서명을 변경하고

ThreadStart thread = delegate { Flush(LogEntries.ToArray()); }; 
     new Thread(thread).Start(); 
     //Flush(LogEntries);   
     LogEntries.Clear(); 
관련 문제