Общий пул объектов без Thread.Sleep? - PullRequest
2 голосов
/ 11 октября 2009

Я разработал «пул объектов» и, похоже, не могу сделать это без использования Thread.Sleep (), что, по моему мнению, является «плохой практикой».

Это относится к моему другому вопросу " Существует ли стандартный способ реализации проприетарного пула соединений в .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 раз. Если поток получает объект из пула, он удерживается, если в течение 2000 мс, если нет, он переходит к следующей итерации. Теперь логика диктует, что 9 потоков будут ожидание объекта в любой момент времени. 9 x 2000 мс - это 18 000 мс, что является максимальным временем, в течение которого поток должен ждать объект. Мой тайм-аут для получения установлен равным 60 000 мс, поэтому ни один поток никогда не должен останавливаться. Однако некоторые так что-то не так, и я подозреваю, что это Thread.Sleep)

Ответы [ 2 ]

5 голосов
/ 11 октября 2009

Поскольку вы уже используете lock, рассмотрите возможность использования Monitor.Wait и Monitor.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);
}
2 голосов
/ 11 октября 2009

вы должны использовать семафор.

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

UPDATE: Семафоры являются одной из базовых конструкций многопоточного программирования. Семафор можно использовать по-разному, но основная идея заключается в том, что когда у вас ограниченный ресурс и много клиентов, которые хотят использовать этот ресурс, вы можете ограничить число клиентов, которые могут получить доступ к ресурсу в любой момент времени.

ниже - очень грубый пример. Я не добавил проверки ошибок или блоки try / 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();
    }
}
...