2009-10-10 9 views
2

"개체 풀"을 개발했으며 Thread.Sleep()을 사용하지 않고는 "나쁜 연습"이라고 생각하지 않습니다.Thread.Sleep없는 공유 객체 풀?

내 다른 질문 "Is there a standard way of implementing a proprietary connection pool in .net?"과 관련이 있습니다. 개체 풀의 배경은 데이터베이스 연결에 사용되는 연결 풀 뒤에있는 아이디어와 비슷합니다. 그러나, 제 경우에는 그것을 표준 ASP.NET 웹 서비스 (IIS6에서 실행)에서 제한된 리소스를 공유하는 데 사용하고 있습니다. 즉, 많은 스레드가이 제한된 리소스에 대한 액세스를 요청할 것입니다. 풀은 이러한 객체를 추출하고 ("Get"), 일단 사용 가능한 풀 객체가 모두 사용되면 다음 요청 스레드는이 객체 중 하나가 다시 사용 가능하게 될 때까지 기다립니다 . 개체가이 설정 한 시간에 사용할 수있게하지 않는 경우, 한 번) 대상으로 수행 "넣어", 타임 아웃 오류가 발생합니다 여기

는 코드입니다.

public class SimpleObjectPool 
{ 
    private const int cMaxGetTimeToWaitInMs = 60000; 
    private const int cMaxGetSleepWaitInMs = 10; 
    private object fSyncRoot = new object(); 
    private Queue<object> fQueue = new Queue<object>(); 

    private SimpleObjectPool() 
    { 
    } 

    private static readonly SimpleObjectPool instance = new SimpleObjectPool(); 
    public static SimpleObjectPool Instance 
    { 
     get 
     { 
      return instance; 
     } 
    } 

    public object Get() 
    { 
     object aObject = null; 
     for (int i = 0; i < (cMaxGetTimeToWaitInMs/cMaxGetSleepWaitInMs); i++) 
     { 
      lock (fSyncRoot) 
      { 
       if (fQueue.Count > 0) 
       { 
        aObject = fQueue.Dequeue(); 
        break; 
       } 
      } 
      System.Threading.Thread.Sleep(cMaxGetSleepWaitInMs); 
     } 
     if (aObject == null) 
      throw new Exception("Timout on waiting for object from pool"); 
     return aObject; 
    } 

    public void Put(object aObject) 
    { 
     lock (fSyncRoot) 
     { 
      fQueue.Enqueue(aObject); 
     } 
    } 
} 

사용을 사용하려면, 다음 중 하나를 수행 할 수 있습니다.

 public void ExampleUse() 
     { 
      PoolObject lTestObject = (PoolObject)SimpleObjectPool.Instance.Get(); 
      try 
      { 
       // Do something... 
      } 
      finally 
      { 
       SimpleObjectPool.Instance.Put(lTestObject); 
      } 
     } 

지금 제가 갖고있는 질문은 입니다 : 어떻게 이것을 쓰면 Thread.Sleep()을 없앨 수 있습니까?

(내가 왜 이것을하고 싶은지는 테스트에서 "거짓"시간 초과가 발생했다고 생각하기 때문입니다. 내 테스트 응용 프로그램에는 3 개의 객체가있는 객체 풀이 있습니다 .12 개의 스레드가 회전합니다. 각 스레드는 풀에서 객체를 100 번 가져옵니다. 스레드가 객체를 풀에서 가져 오면 2,000ms 동안 유지하고, 그렇지 않으면 다음 반복으로 넘어갑니다. 어느 시점에서든 객체를 기다리는 중일 때 .9 x 2000 ms는 18,000 ms이며, thread가 객체를 기다려야하는 최대 시간이다. get timeout은 60,000 ms로 설정되어있어, thread가 타임 아웃 할 필요는 없다. 뭔가 잘못 됐어. 그리고 나는 그 스레드를 의심해. 잠깐.

+0

단일 공유 자원의 여러 독자가 세마포어처럼 들립니다. 왜 그걸 사용하지 않니? – Amirshk

+1

다른 사람들이해야 할 일에 답했습니다. 여기서 Sleep()이 나쁜 생각 인 이유에 대해서만 언급 할 것입니다. 스레드가 깨어있을 때 풀에 객체가있을 것이라는 것을 보장하는 것은 아무것도 없습니다. 따라서 약간의 "행운"을 가진 스레드는 다른 스레드가 풀링 된 객체를 선택하는 동안 잠자고있게 할 수 있습니다. 필요한 것은 객체가 사용 가능하게 될 때 스레드를 깨울 대기 메커니즘입니다. –

답변

5

이미 lock을 사용하고 있으므로 c onsider Get()에서 Monitor.WaitMonitor.Pulse

를 사용하여 :

lock (fSyncRoot) 
{ 
    while (fQueue.Count < 1) 
    Monitor.Wait(fSyncRoot); 

    aObject = fQueue.Dequeue(); 
} 

그리고 Put()에 : 당신이 세마포어를 사용한다

lock (fSyncRoot) 
{ 
    fQueue.Enqueue(aObject); 
    if (fQueue.Count == 1) 
     Monitor.Pulse(fSyncRoot); 
} 
+0

내 기존 코드를 많이 변경하지 않는 우수하고 간단한 솔루션입니다. 나는 다른 사람들이 설명했듯이 세마포어를 사용해야한다. 그러나 이것은 나의 테스트가 보여준 것처럼 훌륭하게 할 것이다. 헨크 감사합니다. – VinceJS

+0

이것은 (일종의) 세마포어입니다. 모니터는 완전히 관리되는 코드이며 첫 번째 선택이어야하며 필요할 때만 '무거운'것을 사용하십시오. –

2

.

http://msdn.microsoft.com/en-us/library/system.threading.semaphore.aspx

UPDATE : 세마포어 멀티 스레드 프로그램의 기본 구조 중 하나이다. 세마포어는 여러 가지 방법으로 사용할 수 있지만 기본적인 아이디어는 제한된 리소스와 해당 리소스를 사용하려는 많은 클라이언트가있을 때 주어진 시간에 리소스에 액세스 할 수있는 클라이언트 수를 제한 할 수 있다는 것입니다.

은 매우 미숙 한 예입니다. 오류 검사를 추가하지 않았거나/finally 블록을 시도했지만 사용자가해야합니다.

또한 확인할 수 있습니다 http://en.wikipedia.org/wiki/Semaphore_(programming)

는 10 버킷 그 양동이를 사용하려면 100 명 말해봐. 대기열에서 버킷을 나타낼 수 있습니다.시작시

, 이제 버킷 큐를 보호하기 위해 세마포어를 만들 큐

for(int i=0;i<10;i++) 
{ 
    B.Push(new Bucket()); 
} 

에 버킷을 모두 추가 할 수 있습니다. 이 세마포어가 트리거 항목이없는 큐에 액세스하기 전에 세마포어를 확인해야합니다 (10)

Semaphore s = new Semaphore(0, 10); 

모든 클라이언트의 용량으로 작성됩니다. 아래 스레드 메소드를 실행하는 100 개의 스레드가있을 수 있습니다. 처음 10 개는 세마포어를 통과합니다. 다른 모든 사람들은 기다릴 것이다.

void MyThread() 
{ 
    while(true) 
    { 
     // thread will wait until the semaphore is triggered once 
     // there are other ways to call this which allow you to pass a timeout 
     s.WaitOne(); 

     // after being triggered once, thread is clear to get an item from the queue 
     Bucket b = null; 

     // you still need to lock because more than one thread can pass the semaphore at the sam time. 
     lock(B_Lock) 
     { 
      b = B.Pop(); 
     } 

     b.UseBucket(); 

     // after you finish using the item, add it back to the queue 
     // DO NOT keep the queue locked while you are using the item or no other thread will be able to get anything out of it    
     lock(B_Lock) 
     { 
      B.Push(b); 
     } 

     // after adding the item back to the queue, trigger the semaphore and allow 
     // another thread to enter 
     s.Release(); 
    } 
} 
+0

네, 당신이 옳았다는 것을 문서에서 보았습니다. 그러나 나는 그 주위에서 뇌를 얻지 못하고 있습니다. – VinceJS