2

私は「オブジェクトプール」を開発しましたが、「悪い習慣」であるThread.Sleep()を使用せずにそれを行うことはできないようです。

これは、私の他の質問「.netに独自の接続プールを実装する標準的な方法はありますか?」に関連しています。オブジェクトプールの背後にある考え方は、データベース接続に使用される接続プールの背後にある考え方と似ています。ただし、私の場合は、標準のASP.NET Webサービス(IIS6で実行)で限られたリソースを共有するために使用しています。これは、多くのスレッドがこの限られたリソースへのアクセスを要求することを意味します。プールはこれらのオブジェクトをディッシュし(「Get」)、使用可能なすべてのプールオブジェクトが使用されると、次のスレッドは、これらのオブジェクトの1つが再び使用可能になるまで、設定された時間だけ待機します(スレッドはオブジェクトで一度実行された「プット」)。この設定時間内にオブジェクトが使用可能にならない場合、タイムアウトエラーが発生します。

コードは次のとおりです。

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()を取り除くためにこれをどのように書くのですか?

(これを実行したいのは、テストで取得している「false」タイムアウトの原因であると思われるためです。テストアプリケーションには、3つのオブジェクトを含むオブジェクトプールがあります。12スレッドをスピンアップし、各スレッドが取得します。プールからオブジェクトを100回取得します。スレッドがプールからオブジェクトを取得した場合、2,000ミリ秒の間保持し、取得しなかった場合は次の反復に進みます。ロジックは、9つのスレッドが待機することを指示します。 9 x 2,000msは18,000msで、これはスレッドがオブジェクトを待機する必要がある最大時間です。gettimeoutは60,000 msに設定されているため、スレッドはタイムアウトしません。間違っていて、そのThread.Sleepが疑われます)

4

2 に答える 2

5

すでに使用しているlockのでMonitor.WaitMonitor.Pulse

Get()

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);
}
于 2009-10-11T00:08:25.070 に答える
2

セマフォを使用する必要があります。

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

更新:セマフォは、マルチスレッドプログラミングの基本的な構成要素の1つです。セマフォはさまざまな方法で使用できますが、基本的な考え方は、リソースが限られていて、そのリソースを使用したいクライアントが多い場合、いつでもリソースにアクセスできるクライアントの数を制限できるということです。

以下は非常に大雑把な例です。エラーチェックやtry/finalブロックを追加しませんでしたが、追加する必要があります。

次も確認できます: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);

すべてのクライアントは、キューにアクセスする前にセマフォを確認する必要があります。以下のthreadメソッドを実行しているスレッドが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();
    }
}
于 2009-10-11T00:23:36.930 に答える