Jump to content

Шаблон пула объектов

(Перенаправлено из пула объектов )

Шаблон пула объектов — это шаблон проектирования программного обеспечения , который использует набор инициализированных объектов , готовых к использованию — « пул » — вместо того, чтобы выделять и уничтожать их по требованию. Клиент пула запросит объект из пула и выполнит операции с возвращенным объектом. Когда клиент завершает работу, он возвращает объект в пул, а не уничтожает его ; это можно сделать вручную или автоматически.

Пулы объектов в основном используются для повышения производительности: в некоторых случаях пулы объектов значительно повышают производительность. Пулы объектов усложняют срок службы объектов , поскольку объекты, полученные из пула и возвращенные в него, фактически не создаются и не уничтожаются в это время и, следовательно, требуют осторожности при реализации.

Описание

[ редактировать ]

Когда необходимо работать с многочисленными объектами, создание экземпляров которых особенно затратно, и каждый объект необходим только в течение короткого периода времени, производительность всего приложения может ухудшиться. В таких случаях может оказаться желательным шаблон проектирования пула объектов.

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

В некоторых пулах объектов ресурсы ограничены, поэтому указывается максимальное количество объектов. Если это число достигнуто и запрошен новый элемент, может быть выдано исключение или поток будет заблокирован до тех пор, пока объект не будет выпущен обратно в пул.

Шаблон проектирования пула объектов используется в нескольких местах стандартных классов .NET Framework. Одним из примеров является поставщик данных .NET Framework для SQL Server. Поскольку соединения с базой данных SQL Server могут создаваться медленно, поддерживается пул соединений. Закрытие соединения фактически не освобождает ссылку на SQL Server. Вместо этого соединение хранится в пуле, из которого его можно получить при запросе нового соединения. Это существенно увеличивает скорость установления соединений.

Преимущества

[ редактировать ]

Объединение объектов в пулы может обеспечить значительный прирост производительности в ситуациях, когда стоимость инициализации экземпляра класса высока, а скорость создания и уничтожения класса высока — в этом случае объекты могут часто использоваться повторно, и каждое повторное использование экономит значительное количество ресурсов. время. Для создания пула объектов требуются ресурсы — память и, возможно, другие ресурсы, такие как сетевые сокеты, поэтому предпочтительно, чтобы количество одновременно используемых экземпляров было небольшим, но это не обязательно.

Объединенный объект получается в предсказуемое время, тогда как создание новых объектов (особенно по сети) может занять разное время. Эти преимущества в основном справедливы для объектов, требующих больших затрат времени, например соединений с базами данных, соединений сокетов, потоков и больших графических объектов, таких как шрифты или растровые изображения.

В других ситуациях простое объединение объектов (которые не содержат внешних ресурсов, а только занимают память) может быть неэффективным и может снизить производительность. [1] В случае простого объединения памяти более подходит метод управления памятью с блочным распределением , поскольку единственной целью является минимизация затрат на выделение и освобождение памяти за счет уменьшения фрагментации.

Выполнение

[ редактировать ]

Пулы объектов могут быть реализованы автоматически на таких языках, как C++, с помощью интеллектуальных указателей . В конструкторе смарт-указателя объект можно запросить из пула, а в деструкторе смарт-указателя объект можно выпустить обратно в пул. В языках со сборкой мусора, где нет деструкторов (которые гарантированно будут вызываться как часть раскрутки стека), пулы объектов должны быть реализованы вручную, путем явного запроса объекта у фабрики и возврата объекта путем вызова метода удаления. (как в шаблоне удаления ). Использование для этого финализатора не является хорошей идеей, так как обычно нет никаких гарантий относительно того, когда (или будет ли) финализатор запущен. Вместо этого следует использовать «попробуйте... наконец», чтобы гарантировать, что получение и освобождение объекта нейтрально к исключениям.

Пулы объектов вручную легко реализовать, но сложнее использовать, поскольку они требуют ручного управления памятью объектов пула.

Обработка пустых пулов

[ редактировать ]

Пулы объектов используют одну из трех стратегий обработки запроса, когда в пуле нет запасных объектов.

  1. Не удалось предоставить объект (и вернуть клиенту ошибку).
  2. Выделите новый объект, увеличив тем самым размер пула. Пулы, которые делают это, обычно позволяют вам установить верхнюю отметку (максимальное количество когда-либо используемых объектов).
  3. В многопоточной среде пул может блокировать клиента до тех пор, пока другой поток не вернет объект в пул.

Подводные камни

[ редактировать ]

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

Устаревшее состояние не всегда может быть проблемой; оно становится опасным, когда заставляет объект вести себя неожиданно. Например, объект, представляющий детали аутентификации, может выйти из строя, если флаг «успешно аутентифицирован» не сброшен перед его повторным использованием, поскольку он указывает, что пользователь аутентифицирован (возможно, как кто-то другой), хотя это не так. Однако если не сбросить значение, используемое только для отладки, например идентификатор последнего использованного сервера аутентификации, проблем не возникнет.

Неправильный сброс объектов может стать причиной утечки информации. Объекты, содержащие конфиденциальные данные (например, номера кредитных карт пользователя), должны быть удалены перед передачей новым клиентам, в противном случае данные могут быть раскрыты неавторизованной стороне.

Если пул используется несколькими потоками, ему могут потребоваться средства, предотвращающие попытки параллельных потоков повторно использовать один и тот же объект параллельно. В этом нет необходимости, если объекты в пуле являются неизменяемыми или иным образом потокобезопасными.

В некоторых публикациях не рекомендуется использовать пулы объектов с некоторыми языками, такими как Java , особенно для объектов, которые используют только память и не содержат внешних ресурсов (например, соединений с базой данных). выделение объектов происходит относительно быстро Оппоненты обычно говорят, что в современных языках со сборщиками мусора ; в то время как оператор new требуется всего десять инструкций, классика new - delete пара, найденная в проектах объединения, требует сотен из них, поскольку она выполняет более сложную работу. Кроме того, большинство сборщиков мусора сканируют «живые» ссылки на объекты, а не память, которую эти объекты используют для своего содержимого. Это означает, что любое количество «мертвых» объектов без ссылок можно отбросить с небольшими затратами. Напротив, сохранение большого количества «живых», но неиспользуемых объектов увеличивает продолжительность сборки мусора. [1]

Следующий код Go инициализирует пул ресурсов указанного размера (параллельная инициализация), чтобы избежать проблем с гонкой ресурсов по каналам, а в случае пустого пула устанавливает обработку тайм-аута, чтобы клиенты не ждали слишком долго.

// package pool
package pool

import (
	"errors"
	"log"
	"math/rand"
	"sync"
	"time"
)

const getResMaxTime = 3 * time.Second

var (
	ErrPoolNotExist  = errors.New("pool not exist")
	ErrGetResTimeout = errors.New("get resource time out")
)

//Resource
type Resource struct {
	resId int
}

//NewResource Simulate slow resource initialization creation
// (e.g., TCP connection, SSL symmetric key acquisition, auth authentication are time-consuming)
func NewResource(id int) *Resource {
	time.Sleep(500 * time.Millisecond)
	return &Resource{resId: id}
}

//Do Simulation resources are time consuming and random consumption is 0~400ms
func (r *Resource) Do(workId int) {
	time.Sleep(time.Duration(rand.Intn(5)) * 100 * time.Millisecond)
	log.Printf("using resource #%d finished work %d finish\n", r.resId, workId)
}

//Pool based on Go channel implementation, to avoid resource race state problem
type Pool chan *Resource

//New a resource pool of the specified size
// Resources are created concurrently to save resource initialization time
func New(size int) Pool {
	p := make(Pool, size)
	wg := new(sync.WaitGroup)
	wg.Add(size)
	for i := 0; i < size; i++ {
		go func(resId int) {
			p <- NewResource(resId)
			wg.Done()
		}(i)
	}
	wg.Wait()
	return p
}

//GetResource based on channel, resource race state is avoided and resource acquisition timeout is set for empty pool
func (p Pool) GetResource() (r *Resource, err error) {
	select {
	case r := <-p:
		return r, nil
	case <-time.After(getResMaxTime):
		return nil, ErrGetResTimeout
	}
}

//GiveBackResource returns resources to the resource pool
func (p Pool) GiveBackResource(r *Resource) error {
	if p == nil {
		return ErrPoolNotExist
	}
	p <- r
	return nil
}

// package main
package main

import (
	"github.com/tkstorm/go-design/creational/object-pool/pool"
	"log"
	"sync"
)

func main() {
	// Initialize a pool of five resources,
	// which can be adjusted to 1 or 10 to see the difference
	size := 5
	p := pool.New(size)

	// Invokes a resource to do the id job
	doWork := func(workId int, wg *sync.WaitGroup) {
		defer wg.Done()
		// Get the resource from the resource pool
		res, err := p.GetResource()
		if err != nil {
			log.Println(err)
			return
		}
		// Resources to return
		defer p.GiveBackResource(res)
		// Use resources to handle work
		res.Do(workId)
	}

	// Simulate 100 concurrent processes to get resources from the asset pool
	num := 100
	wg := new(sync.WaitGroup)
	wg.Add(num)
	for i := 0; i < num; i++ {
		go doWork(i, wg)
	}
	wg.Wait()
}

.NET В библиотеке базовых классов есть несколько объектов, реализующих этот шаблон. System.Threading.ThreadPool настроен на выделение предопределенного количества потоков. Когда потоки возвращаются, они доступны для других вычислений. Таким образом, можно использовать потоки, не платя за их создание и удаление.

Ниже показан базовый код шаблона проектирования пула объектов, реализованный с помощью C#. Для краткости свойства классов объявляются с использованием автоматически реализованного синтаксиса свойств C# 3.0. Их можно заменить полными определениями свойств для более ранних версий языка. Пул показан как статический класс, поскольку необходимость использования нескольких пулов необычна. Однако в равной степени допустимо использовать классы экземпляров для пулов объектов.

namespace DesignPattern.Objectpool;

// The PooledObject class is the type that is expensive or slow to instantiate,
// or that has limited availability, so is to be held in the object pool.
public class PooledObject
{
    private DateTime _createdAt = DateTime.Now;

    public DateTime CreatedAt => _createdAt;

    public string TempData { get; set; }
}

// The Pool class controls access to the pooled objects. It maintains a list of available objects and a 
// collection of objects that have been obtained from the pool and are in use. The pool ensures that released objects 
// are returned to a suitable state, ready for reuse. 
public static class Pool
{
    private static List<PooledObject> _available = new List<PooledObject>();
    private static List<PooledObject> _inUse = new List<PooledObject>();

    public static PooledObject GetObject()
    {
        lock (_available)
        {
            if (_available.Count != 0)
            {
                PooledObject po = _available[0];
                _inUse.Add(po);
                _available.RemoveAt(0);
                return po;
            }
            else
            {
                PooledObject po = new PooledObject();
                _inUse.Add(po);
                return po;
            }
        }
    }

    public static void ReleaseObject(PooledObject po)
    {
        CleanUp(po);

        lock (_available)
        {
            _available.Add(po);
            _inUse.Remove(po);
        }
    }

    private static void CleanUp(PooledObject po)
    {
        po.TempData = null;
    }
}

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

Java поддерживает пул потоков через java.util.concurrent.ExecutorService и другие родственные классы. Служба исполнителя имеет определенное количество «базовых» потоков, которые никогда не отбрасываются. Если все потоки заняты, служба выделяет разрешенное количество дополнительных потоков, которые позже отбрасываются, если не используются в течение определенного времени истечения срока действия. Если больше потоков не разрешено, задачи можно поместить в очередь. Наконец, если эта очередь может стать слишком длинной, ее можно настроить на приостановку запрашивающего потока.

public class PooledObject {
	public String temp1;
	public String temp2;
	public String temp3;
	
	public String getTemp1() {
		return temp1;
	}
	public void setTemp1(String temp1) {
		this.temp1 = temp1;
	}
	public String getTemp2() {
		return temp2;
	}
	public void setTemp2(String temp2) {
		this.temp2 = temp2;
	}
	public String getTemp3() {
		return temp3;
	}
	public void setTemp3(String temp3) {
		this.temp3 = temp3;
	}
}
public class PooledObjectPool {
	private static long expTime = 6000;//6 seconds
	public static HashMap<PooledObject, Long> available = new HashMap<PooledObject, Long>();
	public static HashMap<PooledObject, Long> inUse = new HashMap<PooledObject, Long>();
	
	public synchronized static PooledObject getObject() {
		long now = System.currentTimeMillis();
		if (!available.isEmpty()) {
			for (Map.Entry<PooledObject, Long> entry : available.entrySet()) {
				if (now - entry.getValue() > expTime) { //object has expired
					popElement(available);
				} else {
					PooledObject po = popElement(available, entry.getKey());
					push(inUse, po, now); 
					return po;
				}
			}
		}

		// either no PooledObject is available or each has expired, so return a new one
		return createPooledObject(now);
	}	
	
	private synchronized static PooledObject createPooledObject(long now) {
		PooledObject po = new PooledObject();
		push(inUse, po, now);
		return po;
    }

	private synchronized static void push(HashMap<PooledObject, Long> map,
			PooledObject po, long now) {
		map.put(po, now);
	}

	public static void releaseObject(PooledObject po) {
		cleanUp(po);
		available.put(po, System.currentTimeMillis());
		inUse.remove(po);
	}
	
	private static PooledObject popElement(HashMap<PooledObject, Long> map) {
		 Map.Entry<PooledObject, Long> entry = map.entrySet().iterator().next();
		 PooledObject key= entry.getKey();
		 //Long value=entry.getValue();
		 map.remove(entry.getKey());
		 return key;
	}
	
	private static PooledObject popElement(HashMap<PooledObject, Long> map, PooledObject key) {
		map.remove(key);
		return key;
	}
	
	public static void cleanUp(PooledObject po) {
		po.setTemp1(null);
		po.setTemp2(null);
		po.setTemp3(null);
	}
}

См. также

[ редактировать ]

Примечания

[ редактировать ]
  1. ^ Jump up to: а б Гетц, Брайан (27 сентября 2005 г.). «Теория и практика Java: новые легенды о городской производительности» . ИБМ . IBM DeveloperWorks. Архивировано из оригинала 14 февраля 2012 г. Проверено 15 марта 2021 г.
[ редактировать ]
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: d38896fcf1f2000f4262fd6b41da4d11__1688370300
URL1:https://arc.ask3.ru/arc/aa/d3/11/d38896fcf1f2000f4262fd6b41da4d11.html
Заголовок, (Title) документа по адресу, URL1:
Object pool pattern - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)