Продолжаем тему про пораждающие шаблоны программирования. Рассмотрим интересный шаблон под названием пул объектов. Шаблон представляет набор готовых объектов для работы. Объектный пул применяет, когда создание и уничтожение объекта требуют значительного времени. Часто еще пул применяется в многопоточных приложениях.
Как обычно пример под катом

Для работы пула с объектами нам понадобится интерфейс описывающий объекты содержащиеся в пуле. По сути в интерфейсе необходим только один метод для создания объекта.

    public interface ICreator<T>
    {
        T Create();
    }
Сам пул должен содержать два открытых метода для получения объекта из пула и отправки обратно в пул, с которыми будет происходить работа. Ниже представлен потокобезопасный код пула.

    public class Pool<T> where T: class
    {
        private ArrayList pool; // Список объектов в пуле
        private int count;      // Текущее число объектов
        private int max;        // Максимальное число объектов в пуле 
        private Semaphore semaphore;    // Нужен для потокобезопасности
        private ICreator<T> creator;    // Интерфейсов объектов в пуле

        /// <summary>
        /// Длинна списка доступных объектов из пула
        /// </summary>
        public int Size
        {
            get { return pool.Count; }
        }

        /// <summary>
        /// Максимальное число объектов в пуле 
        /// </summary>
        public int Max
        {
            get { return max; }
            set { max = value; }
        }

        /// <summary>
        /// Текущее число объектов
        /// </summary>
        public int Count
        {
            get { return count; }
        }

        /// <summary>
        /// Конструктор пула
        /// </summary>
        /// <param name="creator">Об</param>
        /// <param name="maxCount"></param>
        public Pool(ICreator<T> creator, int maxCount)
        {
            this.creator = creator;
            max = maxCount;
            count = 0;
            pool = new ArrayList();
            semaphore = new Semaphore(0, max);
        }

        /// <summary>
        /// Создает новый экземпляр, если такого нет в списке объектов пула
        /// </summary>
        /// <returns></returns>
        private T CreateObject()
        {
            T newObject = creator.Create();
            count++;
            return newObject;
        }

        /// <summary>
        /// Получение объекта из пула
        /// </summary>
        /// <returns></returns>
        public T Take()
        {
            lock (pool)
            {
                T thisObject = RemoveObject();
                if (thisObject != null)
                    return thisObject;

                if (count < max)
                    return CreateObject();
            }
            semaphore.WaitOne();
            return Take();
        }

        /// <summary>
        /// Удаляет объект из списка объектов, находящихся в пуле
        /// </summary>
        /// <returns></returns>
        private T RemoveObject()
        {
            while (pool.Count > 0)
            {
                var refThis = (WeakReference)pool[pool.Count - 1];
                pool.RemoveAt(pool.Count - 1);
                var thisObject = (T)refThis.Target;
                if (thisObject != null)
                    return thisObject;
                count--;
            }
            return null;
        }

        /// <summary>
        /// Возвращает объект в пул объектов
        /// </summary>
        /// <param name="obj"></param>
        public void Put(T obj)
        {
            if (obj == null)
                throw new NullReferenceException();
            lock (pool)
            {
                var refThis = new WeakReference(obj);            
                pool.Add(refThis);
                semaphore.Release();
            }
        }
    }
И наконец опишем класс, с которым будет работать пул.
    public class InstanceCreator : ICreator<Instance>
    {
        public Instance Create()
        {
            var result = new Instance {Property = "Test"};
            return result;
        }
    }

    public class Instance
    {
        public String Property { get; set; }
    }
Теперь пример работы с пулом. Четыре потока поочередно берут из пула объект и кладут его обратно. Один из потоков thread1 изменяет свойство Property. Пока объект находится у какого-то потока, остальные потоки ждут пока объект не будет положен обратно в пул.
            var instancePool = new Pool<Instance>(new InstanceCreator(), 1);
            {
                var thread1 = new Thread(() =>
                                            {
                                                Console.WriteLine("Thread #1: started");

                                                Console.WriteLine("Thread #1: Wait object {0}", DateTime.Now.TimeOfDay);
                                                var obj1 = instancePool.Take();
                                                obj1.Property = "asdsad";
                                                Console.WriteLine("Thread #1: Take object {0}", DateTime.Now.TimeOfDay);

                                                Console.WriteLine("Thread #1: Sleep 3000 ms");
                                                Thread.Sleep(3000);

                                                Console.WriteLine("Thread #1: Put object");
                                                instancePool.Put(obj1);
                                            });
                var thread2 = new Thread(() =>
                                            {
                                                Console.WriteLine("Thread #2: started");

                                                Console.WriteLine("Thread #2: Wait object {0}", DateTime.Now.TimeOfDay);
                                                var obj1 = instancePool.Take();
                                                Console.WriteLine("Thread #2: Take object {0}", DateTime.Now.TimeOfDay);

                                                Console.WriteLine("Thread #2: Sleep 2000 ms");
                                                Thread.Sleep(2000);

                                                Console.WriteLine("Thread #2: Put object");
                                                instancePool.Put(obj1);
                                            });

                var thread3 = new Thread(() =>
                                            {
                                                Console.WriteLine("Thread #3: started");

                                                Console.WriteLine("Thread #3: Wait object {0}", DateTime.Now.TimeOfDay);
                                                var obj1 = instancePool.Take();
                                                Console.WriteLine("Thread #3: Take object {0}", DateTime.Now.TimeOfDay);

                                                Console.WriteLine("Thread #3: Sleep 2000 ms");
                                                Thread.Sleep(2000);

                                                Console.WriteLine("Thread #3: Put object");
                                                instancePool.Put(obj1);
                                            });

                var thread4 = new Thread(() =>
                                            {
                                                Console.WriteLine("Thread #4: started");

                                                Console.WriteLine("Thread #4: Wait object {0}", DateTime.Now.TimeOfDay);
                                                var obj1 = instancePool.Take();
                                                Console.WriteLine("Thread #4: Take object {0}", DateTime.Now.TimeOfDay);

                                                Console.WriteLine("Thread #4: Sleep 2000 ms");
                                                Thread.Sleep(2000);

                                                Console.WriteLine("Thread #4: Put object");
                                                instancePool.Put(obj1);
                                            });

                thread1.Start();
                thread2.Start();
                thread3.Start();
                thread4.Start();

                Console.ReadKey();
            }

Last edited Aug 29, 2011 at 10:38 AM by sky_dweller, version 1

Comments

No comments yet.