2009-04-16 2 views
4

이 방법에 대해 실제로 확신 할 수는 없지만 사용자 지정 클래스 내에서 발생하는 이벤트를 구독하고 이상적으로는 대기열에 넣고 먼저 처리해야합니다. 그들이 들어올 때 밖으로 나간다. 나는 Queue<T>을 알고 있으며 이것을 사용해야한다고 생각 하는가? 내 질문은 이벤트 처리기에 내 메시지가 수신되면, 단순히 Enqueue() 대기열에 있습니까? 그렇다면 새 대기열이 어떻게 추가 될까요?새 요소를 수신 할 때 계속해서 스레드의 목록을 반복합니다.

내가 (당신의 모자에게 보류) 같은 것을 수행하는 생성자의 메소드 호출 고려하고

: 확실히이 일을 더 우아한 방법이있을

while (true) 
{ 
    foreach (var item in myQueue) 
    { 
    // process 
    myQueue.Dequeue(); 
    } 
} 

를? 이것은 효과적으로 myQueue를 치고 요소를 포함하고 반복하면서 원하는대로 수행해야합니다. 성능은 어떻게 될 것입니까? 스레드 차단을 피하기 위해이 스레드를 별도의 스레드에 생성 할 수 있습니다. 실제로는 받아 들일 시간이있었습니다. while (true)!

답변

2

여러 스레드에서이 작업을 실행하는 경우 : 당신이 정말로 큐를 사용하는 경우, 당신이 정말로 그것을 반복하지, 큐에서 항목을 당겨해야 대기열의 동기화 문제를 방지하기 위해 잠금 기능을 제공합니다. 엘리먼트를 대기열에 넣을 때 큐를 잠그고 엘리먼트를 큐에서 제거해야합니다.

대기열을 처리하는 스레드가 아무 것도하지 않을 경우 대기열이 현재 비어있는 경우를 제외하고는 기본 코드가 괜찮을 것입니다. 다음과 같이 추가해야합니다 :

while (myQueue.Count == 0) 
    Thread.Sleep(sleepTime); 

이렇게하면 이벤트가 대기열에 가득 찰 때까지 "대기"할 수 있습니다.

또한 큐에서 큐를 dequeue하면 foreach를 사용할 수 없습니다. 당신은 같은 것을 수행 할 수 있습니다 : 누군가가 당신의 큐에 추가하는 경우 수집 수정 문제를 방지 할 수

while (myQueue.Count == 0) 
    Thread.Sleep(sleepTime); 
while (myQueue.Count > 0) 
{ 
    lock(myLock) 
     myObject = myQueue.Dequeue(); 
    // do your work... 
} 

이, 그리고 당신은 당신이 당신의 요소를 처리하는 전체 시간을 잠글 필요에서 유지.


편집 : 저는 이것이 항상 가장 효과적인 전략은 아니라는 의견에 동의합니다.

대기열이 주로 비어 있고 경우에만 요소가있는 경우이를 래핑하는 사용자 정의 클래스를 만듭니다.

대기열이 비어 있거나 비어있을 때 이벤트가 트리거 될 수 있습니다. 대기열이 항목을 받자 마자 이벤트가 스레드를 처리하여 처리하고 스레드를 비울 수 있습니다. 0 항목에 도달하자마자 처리를 중지하는 이벤트가 발생할 수 있습니다.

큐의 상태가 대부분 비어있는 경우 지속적으로 대기하는 것보다 훨씬 효과적입니다. 반면에 대기열이 거의 끊임없이 가득 차고 처리 스레드가 거의 유지되지 않으면 위의 접근 방식을 사용하여 단순화합니다.

+0

좋은 제안. 감사 리드. 나는 약간의 생각을 할 것입니다. 그렇습니다. 실은 아무것도 할 수 없지만, 이것에 만족합니다. – GONeale

+0

제발 그렇게하지 마십시오. 바쁜 대기는 성능과 배터리 수명에 좋지 않습니다. 큐에 항목이있을 때까지 기다릴 필요가 있다면, 어떤 종류의 이벤트를 사용해야합니다. –

+0

그건 제가 1800을하고 싶습니다. 그리고 그것은 Queue 클래스에서 이벤트 처리기를 체크 한 첫 번째 것입니다. 없음입니다. Queue를 상속 받고 Enqueue 명령을 오버로드하여 내 자신을 만들 수 있다고 생각하십니까? 어떻게 생각해? – GONeale

4

이것은 전형적인 생산자/소비자 문제입니다. 빠른 웹 검색은 정확히 이것을 다루는 http://msdn.microsoft.com/en-us/library/yy12yx1f(VS.80,loband).aspx을 나타냅니다.

시스템의 다른 스레드를 잠재적으로 굶어 죽일 수있는 작업이 없을 때에도 스레드가 CPU의 100 %를 사용하기 때문에 while (true) 루프를 수행하지 않으려합니다.

0

거기에서 뭘하고있는 것처럼 보이지 않습니다. 당신이 어떤 형태를 도입해야합니다

while (!queue.empty()) // or whatever 
{ 
    process the first item in the queue 
} 
+0

@ 1800, Reed의 게시물에 대한 의견이 있습니다. – GONeale

+0

@ 1800, queue.Empty() 및 queue.Count()와 일치하지 않으면 while이 종료됩니다. – GONeale

4

이 작업을 수행 할 수 없습니다. 열거자를 계속 사용하면서 기본 컬렉션을 변경하면 foreach에 대해 반환되는 열거자는 예외를 throw합니다.

기본적으로해야 할 일은 이벤트를 처리 할 다른 스레드를 설정하는 것입니다. 이상적으로는, 이벤트 나 다른 동기화 메커니즘을 통해이 다른 스레드에 사용 가능한 작업이 있음을 알릴 수 있습니다. 작업 항목을 대기열에서 빼내어 처리 한 다음 다음 신호가 들어올 때까지 잠자기합니다. 또는 폴링 (주기적으로 깨우고 다른 항목을 확인)을 사용할 수도 있지만 효율성이 떨어집니다. 어떤 경우 든 잠금을 사용하여 두 스레드에서 동시에 큐를 수정하지 않도록해야합니다.

1

일반적으로 ManualResetEvent를 사용하여 요소가 컬렉션에 추가되었음을 알립니다.

while (true) 
{ 
    myqAddedSignal.WaitOne(); 
    lock (myLock) 
    { 
     // pull everything off myQ into a list 
     // clear myQ 
     myqAddedSignal.Reset(); 
    } 

    foreach (obj in copyOfMyQ) 
    { 
     ... 
    } 
} 

이를 : 한 번,이 큐를 비 웁니다 신호 신호가 다음 항목을 처리 재설정 -

lock (myLock) 
{ 
    myq.Enqueue(...); 
    myqAddedSignal.Set(); 
} 

신호의 처리 스레드 대기 :

이벤트 핸들러는 다음과 같이 수행 대기열의 항목을 스레드로부터 안전하게 처리합니다. 유일한 공유 상태는 myqAddedSignal입니다. myLAddress에 대한 액세스는 myLock에서 동기화됩니다 (대개이 객체를 객체로 만듭니다).

0

Reed가 제공 한 조언에 따라 큐를 비었을 때 채워진 이벤트가 발생합니다.

사용자 정의 EventQueue<T> 등급 :

public class EventQueue<T> : Queue<T> 
{ 
    public delegate void OnQueueMadeEmptyDelegate(); 
    public event OnQueueMadeEmptyDelegate OnQueueMadeEmpty; 
    public delegate void OnQueueMadeNonEmptyDelegate(); 
    public event OnQueueMadeNonEmptyDelegate OnQueueMadeNonEmpty; 

    public new void Enqueue(T item) 
    { 
     var oldCount = Count; 
     base.Enqueue(item); 
     if (OnQueueMadeNonEmpty != null && 
      oldCount == 0 && Count > 0) 
      // FIRE EVENT 
      OnQueueMadeNonEmpty(); 
    } 
    public new T Dequeue() 
    { 
     var oldCount = Count; 
     var item = base.Dequeue(); 
     if (OnQueueMadeEmpty != null && 
      oldCount > 0 && Count == 0) 
     { 
      // FIRE EVENT 
      OnQueueMadeEmpty(); 
     } 
     return item; 
    } 
    public new void Clear() 
    { 
     base.Clear(); 
     if (OnQueueMadeEmpty != null) 
     { 
      // FIRE EVENT 
      OnQueueMadeEmpty(); 
     } 
    } 
} 

(나는 < 요약을 제거한>의 작은 코드 샘플 내가 기본 논리에 추가 로직을 추가하는 방법으로 "새로운"수정을 사용하고 있습니다.).

public delegate void InitQueueDelegate(); 
private InitQueueDelegate initQueueDelegate; 

private EventQueue<QueueRequest> translationQueue; 
private Object queueLock = new Object(); 

메인 클래스의 생성자에서 :

initQueueDelegate = this.InitQueue; 
initQueueDelegate.BeginInvoke(null, null); 

메인 클래스 본문 :

메인 클래스의 음부

private void InitQueue() 
{ 
    this.translationQueue = new EventQueue<QueueRequest>(); 
    this.translationQueue.OnQueueMadeEmpty += new EventQueue<QueueRequest>.OnQueueMadeEmptyDelegate(translationQueue_OnQueueMadeEmpty); 
    this.translationQueue.OnQueueMadeNonEmpty += new EventQueue<QueueRequest>.OnQueueMadeNonEmptyDelegate(translationQueue_OnQueueMadeNonEmpty); 
} 

void translationQueue_OnQueueMadeNonEmpty() 
{ 
    while (translationQueue.Count() > 0) 
    { 
     lock (queueLock) 
     { 
      QueueRequest request = translationQueue.Dequeue(); 
#if DEBUG 
      System.Diagnostics.Debug.WriteLine("Item taken from queue..."); 
#endif 
      // hard work 
      .... 
      .... 
      .... 
     } 
    } 
} 

void translationQueue_OnQueueMadeEmpty() 
{ 
    // empty queue 
    // don't actually need to do anything here? 
} 

private void onMessageReceived(....) 
{ 
    .... 
    .... 
    .... 
    // QUEUE REQUEST 
    lock (queueLock) 
    { 
    QueueRequest queueRequest = new QueueRequest 
            { 
             Request = request, 
             Sender = sender, 
             Recipient = tcpClientService 
            }; 
    translationQueue.Enqueue(queueRequest); 
#if DEBUG 
    System.Diagnostics.Debug.WriteLine("Item added to queue..."); 
#endif 
    } 
} 

그리고 마지막으로 QueueRequest 구조체 :

public struct QueueRequest 
{ 
    public MessageTranslateRequest Request { get; set; } 
    public TCPClientService Sender { get; set; } 
    public TCPClientService Recipient { get; set; } 
} 

거기에 많은 정보가 있지만 전체 구현을 확인하길 원했습니다. 어떻게 생각해? 어떻게 올바른 잠금을 수행 했습니까?

내 솔루션이 그의 아이디어에서 만들어 졌기 때문에 Reed에게 감사의 뜻을 전합니다.

0

찾고있는 구조는 세마포어입니다.

대기열에 항목을 추가 한 다음 세마포어에 카운트를 추가합니다.

다른 스레드의 세마포를 기다리고 대기열에서 항목을 처리합니다.

큐 구현 (BCL과 유사)에 따라 검색하는 동안 잠금/잠금 해제해야합니다.

관련 문제