Объектный пул: различия между версиями

Материал из Поле цифровой дидактики
 
 
(не показано 5 промежуточных версий этого же участника)
Строка 1: Строка 1:
{{значения|Пул (значения)}}
'''Объектный пул''' порождающий шаблон проектирования, набор инициализированных и готовых к использованию объектов. Когда системе требуется объект, он не создаётся, а берётся из пула. Когда объект больше не нужен, он не уничтожается, а возвращается в пул.
{{Шаблон проектирования
|название = Объектный пул
|английское_название = Object pool
|диаграмма = 
|тип = порождающий
|назначение =
|структура =
|применяется_в_случаях =
|плюсы =
|минусы =
|родственные_шаблоны =
|design_patterns = Нет
}}
'''Объектный пул''' ({{lang-en|object pool}}) — [[Порождающие шаблоны проектирования|порождающий шаблон проектирования]], набор инициализированных и готовых к использованию объектов. Когда системе требуется объект, он не создаётся, а берётся из пула. Когда объект больше не нужен, он не уничтожается, а возвращается в пул.


== Применение ==
== Применение ==
Строка 30: Строка 16:
# Информация о видимых объектах во многих [[компьютерная игра|компьютерных играх]] (хорошим примером является [[движок Doom]]). Эта информация актуальна только в течение одного кадра; после того, как кадр выведен, список опустошается.
# Информация о видимых объектах во многих [[компьютерная игра|компьютерных играх]] (хорошим примером является [[движок Doom]]). Эта информация актуальна только в течение одного кадра; после того, как кадр выведен, список опустошается.
# Компьютерная игра для хранения всех объектов на карте, вместо того, чтобы использовать обычные механизмы распределения памяти, может завести [[массив (программирование)|массив]] такого размера, которого заведомо хватит на все объекты, и свободные ячейки держать в виде [[связный список|связного списка]]. Такая конструкция повышает скорость, уменьшает фрагментацию памяти и снижает нагрузку на [[Сборка мусора (программирование)|сборщик мусора]] (если он есть).
# Компьютерная игра для хранения всех объектов на карте, вместо того, чтобы использовать обычные механизмы распределения памяти, может завести [[массив (программирование)|массив]] такого размера, которого заведомо хватит на все объекты, и свободные ячейки держать в виде [[связный список|связного списка]]. Такая конструкция повышает скорость, уменьшает фрагментацию памяти и снижает нагрузку на [[Сборка мусора (программирование)|сборщик мусора]] (если он есть).
== Ловушки ==
{{anchor|Объектная_клоака}}
# После того, как объект возвращён, он должен вернуться в состояние, пригодное для дальнейшего использования. Если объекты после возвращения в пул оказываются в неправильном или неопределённом состоянии, такая конструкция называется '''объектной клоакой''' ({{lang-en|object cesspool}}).
# Повторное использование объектов также может привести к утечке информации. Если в объекте есть секретные данные (например, номер [[кредитная карточка|кредитной карты]]), после освобождения объекта эту информацию надо затереть.
# Многопоточный объектный пул написать не так просто.
# На 2020-е годы в языках со сбором мусора управление памятью хорошо оптимизировано под постоянное выделение-отдачу. Так что, если объект занимает только память, руководства по Java не рекомендуют пользоваться пулами: обычный <code>new</code> требует всего десять процессорных команд. А сборщики мусора часто сканируют ссылки на объекты, а не их память — потому чем больше в памяти «живых» объектов, тем ниже производительность такого сборщика.


== Пример реализации ==
== Пример реализации ==


=== Пример на [[Python]] ===
=== Пример на [[Python]] ===
{{Hider_hiding
 
| title = Исходный текст на языке Python
| content =
<source lang='python'>  
<source lang='python'>  
#coding: utf-8
#coding: utf-8
Строка 133: Строка 110:


=== Пример на [[C++]] ===
=== Пример на [[C++]] ===
{{Hider_hiding
 
| title = Исходный текст на языке C++
| content =
<source lang="cpp">
<source lang="cpp">
#include <vector>
#include <vector>
Строка 211: Строка 186:
}
}
</source>
</source>
}}
Из примера для простоты убраны [[Шаблоны C++|шаблоны]] и потокозащищенность. При необходимости использования пула в нескольких потоках следует защитить тело методов createNewObject и deleteObject от одновременного выполнения каким-либо подходящим объектом синхронизации, например, [[Критическая секция|критической секцией]] или [[мьютекс]]ом.
=== Пример на [[C Sharp#|C#]] ===
{{Hider_hiding
| title = Исходный текст на языке C#
| content =
<source lang="csharp">
namespace Digital_Patterns.Creational.Object_Pool.Soft
{
    /// <summary>
    /// Интерфейс для использования шаблона "Object Pool" <see cref="Object_Pool"/>
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public interface ICreation<T>
    {
        /// <summary>
        /// Возвращает вновь созданный объект
        /// </summary>
        /// <returns></returns>
        T Create();
    }
}
</source>
}}
{{Hider_hiding
| title = Исходный текст на языке C#
| content =
<source lang="csharp">
using System;
using System.Collections;
using System.Threading;
namespace Digital_Patterns.Creational.Object_Pool.Soft
{
    /// <summary>
    /// Реализация пула объектов, использующего "мягкие" ссылки
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class ObjectPool<T> where T : class
    {
        /// <summary>
        /// Объект синхронизации
        /// </summary>
        private Semaphore semaphore;
        /// <summary>
        /// Коллекция содержит управляемые объекты
        /// </summary>
        private ArrayList pool;
        /// <summary>
        /// Ссылка на объект, которому делегируется ответственность
        /// за создание объектов пула
        /// </summary>
        private ICreation<T> creator;
        /// <summary>
        /// Количество объектов, существующих в данный момент
        /// </summary>
        private Int32 instanceCount;
        /// <summary>
        /// Максимальное количество управляемых пулом объектов
        /// </summary>
        private Int32 maxInstances;
        /// <summary>
        /// Создание пула объектов
        /// </summary>
        /// <param name="creator">Объект, которому пул будет делегировать ответственность
        /// за создание управляемых им объектов</param>
        public ObjectPool(ICreation<T> creator)
            : this(creator, Int32.MaxValue)
        {
        }
        /// <summary>
        /// Создание пула объектов
        /// </summary>
        /// <param name="creator">Объект, которому пул будет делегировать ответственность
        /// за создание управляемых им объектов</param>
        /// <param name="maxInstances">Максимальное количество экземпляров классов,
        /// которым пул разрешает существовать одновременно
        /// </param>
        public ObjectPool(ICreation<T> creator, Int32 maxInstances)
        {
            this.creator = creator;
            this.instanceCount = 0;
            this.maxInstances = maxInstances;
            this.pool = new ArrayList();
            this.semaphore = new Semaphore(0, this.maxInstances);
        }
        /// <summary>
        /// Возвращает количество объектов в пуле, ожидающих повторного
        /// использования. Реальное количество может быть меньше
        /// этого значения, поскольку возвращаемая
        /// величина - это количество "мягких" ссылок в пуле.
        /// </summary>
        public Int32 Size
        {
            get
            {
                lock(pool)
                {
                    return pool.Count;
                }
            }
        }
        /// <summary>
        /// Возвращает количество управляемых пулом объектов,
        /// существующих в данный момент
        /// </summary>
        public Int32 InstanceCount { get { return instanceCount; } }
        /// <summary>
        /// Получить или задать максимальное количество управляемых пулом
        /// объектов, которым пул разрешает существовать одновременно.
        /// </summary>
        public Int32 MaxInstances
        {
            get { return maxInstances; }
            set { maxInstances = value; }
        }
        /// <summary>
        /// Возвращает из пула объект. При пустом пуле будет создан
        /// объект, если количество управляемых пулом объектов не
        /// больше или равно значению, возвращаемому методом
        /// <see cref="ObjectPool{T}.MaxInstances"/>. Если количество управляемых пулом
        /// объектов превышает это значение, то данный метод возварщает null
        /// </summary>
        /// <returns></returns>
        public T GetObject()
        {
            lock(pool)
            {
                T thisObject = RemoveObject();
                if (thisObject != null)
                    return thisObject;
                if (InstanceCount < MaxInstances)
                    return CreateObject();
               
                return null;
            }
        }
        /// <summary>
        /// Возвращает из пула объект. При пустом пуле будет создан
        /// объект, если количество управляемых пулом объектов не
        /// больше или равно значению, возвращаемому методом
        /// <see cref="ObjectPool{T}.MaxInstances"/>. Если количество управляемых пулом
        /// объектов превышает это значение, то данный метод будет ждать до тех
        /// пор, пока какой-нибудь объект не станет доступным для
        /// повторного использования.
        /// </summary>
        /// <returns></returns>
        public T WaitForObject()
        {
            lock(pool)
            {
                T thisObject = RemoveObject();
                if (thisObject != null)
                    return thisObject;
                if (InstanceCount < MaxInstances)
                    return CreateObject();
            }
            semaphore.WaitOne();
            return WaitForObject();
        }
        /// <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;
                instanceCount--;
            }
            return null;
        }
        /// <summary>
        /// Создать объект, управляемый этим пулом
        /// </summary>
        /// <returns></returns>
        private T CreateObject()
        {
            T newObject = creator.Create();
            instanceCount++;
            return newObject;
        }
        /// <summary>
        /// Освобождает объект, помещая его в пул для
        /// повторного использования
        /// </summary>
        /// <param name="obj"></param>
        /// <exception cref="NullReferenceException"></exception>
        public void Release(T obj)
        {
            if(obj == null)
                throw new NullReferenceException();
            lock(pool)
            {
                var refThis = new WeakReference(obj);
                pool.Add(refThis);
                semaphore.Release();
            }
        }
    }
}
</source>
}}
{{Hider_hiding
| title = Исходный текст на языке C#
| content =
<source lang="csharp">
namespace Digital_Patterns.Creational.Object_Pool.Soft
{
    public class Reusable
    {
        public Object[] Objs { get; protected set; }
        public Reusable(params Object[] objs)
        {
            this.Objs = objs;
        }
    }
    public class Creator : ICreation<Reusable>
    {
        private static Int32 iD = 0;
        public Reusable Create()
        {
            ++iD;
            return new Reusable(iD);
        }
    }
    public class ReusablePool : ObjectPool<Reusable>
    {
        public ReusablePool()
            : base(new Creator(), 2)
        {
           
        }
    }
}
</source>
}}
{{Hider_hiding
| title = Исходный текст на языке C#
| content =
<source lang="csharp">
using System;
using System.Threading;
using Digital_Patterns.Creational.Object_Pool.Soft;
namespace Digital_Patterns
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(System.Reflection.MethodInfo.GetCurrentMethod().Name);
            var reusablePool = new ReusablePool();
            var thrd1 = new Thread(Run);
            var thrd2 = new Thread(Run);
            var thisObject1 = reusablePool.GetObject();
            var thisObject2 = reusablePool.GetObject();
            thrd1.Start(reusablePool);
            thrd2.Start(reusablePool);
            ViewObject(thisObject1);
            ViewObject(thisObject2);
            Thread.Sleep(2000);
            reusablePool.Release(thisObject1);
            Thread.Sleep(2000);
            reusablePool.Release(thisObject2);
            Console.ReadKey();
        }
        private static void Run(Object obj)
        {
            Console.WriteLine("\t" + System.Reflection.MethodInfo.GetCurrentMethod().Name);
            var reusablePool = (ReusablePool)obj;
            Console.WriteLine("\tstart wait");
            var thisObject1 = reusablePool.WaitForObject();
            ViewObject(thisObject1);
            Console.WriteLine("\tend wait");
            reusablePool.Release(thisObject1);
        }
        private static void ViewObject(Reusable thisObject)
        {
            foreach (var obj in thisObject.Objs)
            {
                Console.Write(obj.ToString() + @" ");
            }
            Console.WriteLine();
        }
    }
}
</source>
}}
=== Пример на [[VB.NET]] ===
{{Hider_hiding
| title = Исходный текст на языке VB.NET
| content =
<source lang="vbnet">
Namespace Digital_Patterns.Creational.Object_Pool.Soft
    ' Интерфейс для использования шаблона "Object Pool" <see cref="Object_Pool"/>
    Public Interface ICreation(Of T)
        ' Возвращает вновь созданный объект
        Function Create() As T
    End Interface
End Namespace
</source>
}}
{{Hider_hiding
| title = Исходный текст на языке VB.NET
| content =
<source lang="vbnet">
Namespace Digital_Patterns.Creational.Object_Pool.Soft
    'Реализация пула объектов, использующий "мягкие" ссылки
    Public Class ObjectPool(Of T As Class)
        'Объект синхронизации
        Private semaphore As Semaphore
        'Коллекция содержит управляемые объекты
        Private pool As ArrayList
        'Ссылка на объект, которому делегируется ответственность за создание объектов пула
        Private creator As ICreation(Of T)
        'Количество объектов, существующих в данный момент
        Private m_instanceCount As Int32
        'Максимальное количество управляемых пулом объектов
        Private m_maxInstances As Int32
        'Созданчие пула объектов
        '  creator - объект, которому пул будет делегировать ответственность за создание управляемых им объектов
        Public Sub New(ByVal creator As ICreation(Of T))
            Me.New(creator, Int32.MaxValue)
        End Sub
        'Создание пула объектов
        '  creator - Объект, которому пул будет делегировать ответственность за создание управляемых им объектов
        '  maxInstances - Максимальное количество экземпляров класс, которым пул разрешает существовать одновременно
        Public Sub New(ByVal creator As ICreation(Of T), ByVal maxInstances As Int32)
            Me.creator = creator
            Me.m_instanceCount = 0
            Me.m_maxInstances = maxInstances
            Me.pool = New ArrayList()
            Me.semaphore = New Semaphore(0, Me.m_maxInstances)
        End Sub
        'Возвращает количество объектов в пуле, ожидающих повторного
        'использования. Реальное количество может быть меньше
        'этого значения, поскольку возвращаемая
        'величина - это количество "мягких" ссылок в пуле.
        Public ReadOnly Property Size() As Int32
            Get
                SyncLock pool
                    Return pool.Count
                End SyncLock
            End Get
        End Property
        'Возвращает количество управляемых пулом объектов,
        'существующих в данный момент
        Public ReadOnly Property InstanceCount() As Int32
            Get
                Return m_instanceCount
            End Get
        End Property
        'Получить или задать максимальное количество управляемых пулом
        'объектов, которым пул разрешает существовать одновременно.
        Public Property MaxInstances() As Int32
            Get
                Return m_maxInstances
            End Get
            Set(ByVal value As Int32)
                m_maxInstances = value
            End Set
        End Property
        'Возвращает из пула объект. При пустом пуле будет создан
        'объект, если количество управляемых пулом объектов не
        'больше или равно значению, возвращаемому методом ObjectPool{T}.MaxInstances.
        'Если количество управляемых пулом объектов превышает это значение, то данный
        'метод возварщает null
        Public Function GetObject() As T
            SyncLock pool
                Dim thisObject As T = RemoveObject()
                If thisObject IsNot Nothing Then
                    Return thisObject
                End If
                If InstanceCount < MaxInstances Then
                    Return CreateObject()
                End If
                Return Nothing
            End SyncLock
        End Function
        ' Возвращает из пула объект. При пустом пуле будет создан
        ' объект, если количество управляемых пулом объектов не
        ' больше или равно значению, возвращаемому методом ObjectPool{T}.MaxInstances
        ' Если количество управляемых пулом объектов превышает это значение,
        ' то данный метод будет ждать до тех пор, пока какой-нибудь объект
        ' не станет доступным для повторного использования.
        Public Function WaitForObject() As T
            SyncLock pool
                Dim thisObject As T = RemoveObject()
                If thisObject IsNot Nothing Then
                    Return thisObject
                End If
                If InstanceCount < MaxInstances Then
                    Return CreateObject()
                End If
            End SyncLock
            semaphore.WaitOne()
            Return WaitForObject()
        End Function
        ' Удаляет объект из коллекции пула и возвращает его
        Private Function RemoveObject() As T
            While pool.Count > 0
                Dim refThis = DirectCast(pool(pool.Count - 1), WeakReference)
                pool.RemoveAt(pool.Count - 1)
                Dim thisObject = DirectCast(refThis.Target, T)
                If thisObject IsNot Nothing Then
                    Return thisObject
                End If
                m_instanceCount -= 1
            End While
            Return Nothing
        End Function
        ' Создать объект, управляемый этим пулом
        Private Function CreateObject() As T
            Dim newObject As T = creator.Create()
            m_instanceCount += 1
            Return newObject
        End Function
        ' Освобождает объект, помещая его в пул для повторного использования
        Public Sub Release(ByVal obj As T)
            If obj Is Nothing Then
                Throw New NullReferenceException()
            End If
            SyncLock pool
                Dim refThis = New WeakReference(obj)
                pool.Add(refThis)
                semaphore.Release()
            End SyncLock
        End Sub
    End Class
End Namespace
</source>
}}
{{Hider_hiding
| title = Исходный текст на языке VB.NET
| content =
<source lang="vbnet">
Namespace Digital_Patterns.Creational.Object_Pool.Soft
    '###  Класс Reusable  ####
    Public Class Reusable
        Private m_Objs As Object()
        Public Sub New(ByVal ParamArray objs As Object())
            Me.Objs = objs
        End Sub
        Public Property Objs() As Object()
            Get
                Return m_Objs
            End Get
            Protected Set(ByVal value As Object())
                m_Objs = value
            End Set
        End Property
    End Class
    '###  Класс Creator  ####
    Public Class Creator
        Implements ICreation(Of Reusable)
        Private Shared iD As Int32 = 0
        Public Function Create() As Reusable Implements ICreation(Of Reusable).Create
            iD += 1
            Return New Reusable(iD)
        End Function
    End Class
    '###  Класс ReusablePool  ####
    Public Class ReusablePool
        Inherits ObjectPool(Of Reusable)
        Public Sub New()
            MyBase.New(New Creator(), 2)
        End Sub
    End Class
End Namespace
</source>
}}
{{Hider_hiding
| title = Исходный текст на языке VB.NET
| content =
<source lang="vbnet">
Imports System.Threading
Imports Digital_Patterns.Creational.Object_Pool.Soft
Namespace Digital_Patterns
    Class Program
        Shared Sub Main()
            Console.WriteLine(System.Reflection.MethodInfo.GetCurrentMethod().Name)
            Dim reusablePool = New ReusablePool()
            Dim thrd1 = New Thread(AddressOf Run)
            Dim thrd2 = New Thread(AddressOf Run)
            Dim thisObject1 = reusablePool.GetObject()
            Dim thisObject2 = reusablePool.GetObject()
            thrd1.Start(reusablePool)
            thrd2.Start(reusablePool)
            ViewObject(thisObject1)
            ViewObject(thisObject2)
            Thread.Sleep(2000)
            reusablePool.Release(thisObject1)
            Thread.Sleep(2000)
            reusablePool.Release(thisObject2)
            Console.ReadKey()
        End Sub
        Private Shared Sub Run(ByVal obj As [Object])
            Console.WriteLine(vbTab & System.Reflection.MethodInfo.GetCurrentMethod().Name)
            Dim reusablePool = DirectCast(obj, ReusablePool)
            Console.WriteLine(vbTab & "start wait")
            Dim thisObject1 = reusablePool.WaitForObject()
            ViewObject(thisObject1)
            Console.WriteLine(vbTab & "end wait")
            reusablePool.Release(thisObject1)
        End Sub
        Private Shared Sub ViewObject(ByVal thisObject As Reusable)
            For Each obj As Object In thisObject.Objs
                Console.Write(obj.ToString() & " ")
            Next
            Console.WriteLine()
        End Sub
    End Class
End Namespace
</source>
}}
=== Пример на [[Perl]] ===
{{Hider_hiding
| title = Исходный текст на языке Perl
| content =
<source lang='perl'>
#!/usr/bin/perl -w
=for comment
Модуль ObjectPool реализовывает паттерн программирования "объектный пул" путём моделирования
поведения лучника, в колчане у которого имеется ограниченное количество стрел, в результате
чего ему приходится периодически их подбирать
Пакет описывает поведение лучника
=cut
package Archer {
   
    use Quiver;    # колчан с стрелами лучника
   
    use strict;
    use warnings;
   
    use constant    ARROWS_NUMBER  =>  5;  # количество стрел в колчане
    use constant    SLEEP_TIME      =>  3;  # максимальный перерыв между двумя действиями (в секундах)
   
    # -- ** констркутор ** --
    sub new {
        my $class = shift;
        my $self = {
            quiver => Quiver->new(ARROWS_NUMBER),  # объект класса "Колчан"
        };
        bless $self, $class;
        return $self;
    }
   
    # -- ** инициализация стрельбы ** --
    sub shooting_start {
        my($self) = (shift);
        while (1) {    # условно бесконечный цикл, в котором ведется стрельба
            $self->shoot() for (0 .. rand(ARROWS_NUMBER - 1));  # случайное количество выстрелов
            $self->reload() for (0 .. rand(ARROWS_NUMBER - 1)); # случайное количество возвратов отстреляных стрел
        }
       
    }
   
    # -- ** выстрел ** --
    sub shoot {
        my($self) = (shift);
        $self->{quiver}->arrow_pull();  # отправляем стрелу куда подальше
        sleep rand(SLEEP_TIME);        # ... и пребываем в ожидании на протяжении неопределенного промежутка времени
    }
   
    # -- ** возвращение выпущеной стрелы ** --
    sub reload {
        my($self) = (shift);
        $self->{quiver}->arrow_return();    # возвращаем выпущенную ранее стрелу
        sleep rand(SLEEP_TIME);            # и опять находимся в ожидании
    }
}
$archer = Archer->new();    # бравый стрелок берет свой колчан со стрелами
$archer->shooting_start();  # ... и начинает стрелять
=for comment
Пакет описывает свойства колчана, которым пользуется лучник (Archer) и в котором хранятся стрелы (Arrow)
=cut
package Quiver {
    use Arrow;      # одна стрела из колчана
    use feature "say";
    use strict;
    use warnings;
   
   
    # -- ** конструктор ** --
    sub new {
        my($class, $arrows_number) = (shift, shift);
        my $self = {
            arrows => [],  # стрелы в колчане (их пока нет, но скоро они там появятся)
        };
        bless $self, $class;
        $self->arrows_prepare($arrows_number);  # загрузить стрелы в колчан
        return $self;
    }
   
    # -- ** приготовление стрел к стрельбе ** --
    sub arrows_prepare {
        my($self, $arrows_number) = (shift, shift);
        push @{$self->{arrows}}, Arrow->new($_) for (0 .. $arrows_number - 1);  # укладываем стрелы в колчан
    }
   
    # -- ** извлечение стрелы из колчана ** --
    sub arrow_pull {
        my($self) = (shift);
        foreach(@{$self->{arrows}}) {  # для каждой стрелы проверяем, в колчане ли она
            if($_->check_state()) {    # и если - да
                $_->pull();            # вынимаем её оттуда (и стреляем)
                last;                  # двумя стрелами одновременно мы выстрелить не сможем
            }
        }
    }
   
    # -- ** возвращение стрелы в колчан ** --
    sub arrow_return {
        my($self) = (shift);
        foreach(@{$self->{arrows}}) {  # для каждой стрелы проверяем, не выпущена ли она уже
            if(!$_->check_state()) {    # если в колчане такой стрелы нет
                $_->return();          # идем и подбираем её
                last;                  # в теории лучник может подобрать больше одной стрелы за раз, но автор считает иначе
            }
        }
    }
}
1;
=for comment
Пакет описывает свойства одной отдельной стрелы, находящейся в колчане (Quiver) лучника (Archer)
=cut
package Arrow {
    use feature "say";
    use strict;
    use warnings;
   
    # -- ** конструктор ** --
    sub new {
        my $class = shift;
        my $self = {
            number => shift,    # номер стрелы
            state => 1,        # состояние стрелы (1 = в колчане, 0 = где-то валяется после выстрела)
        };
        bless $self, $class;
        return $self;
    }
   
    # -- ** изъять стрелу из колчана ** --
    sub pull {
        my($self) = (shift);
        $self->{state} = 0;            # изменить состояние стрелы на "выпущена"
        say "pulled $self->{number}";  # сообщить о том, что выстрел состоялся
    }
   
    # -- ** вернуть стрелу обратно в колчан ** --
    sub return {
        my($self) = (shift);
        $self->{state} = 1;            # изменить состояние стрелы на "в колчане"
        say "returned $self->{number}"; # сообщить о том, что стрела вернулась к лучнику
    }
   
    # -- ** проверить состояние стрелы ** --
    sub check_state {
        my($self) = (shift);
        return $self->{state};          # вернуть состояние стрелы (1 = в колчане, 0 = выпущена)
    }
}
1;
</source>
}}


== Ссылки ==
== Ссылки ==
Строка 982: Строка 193:


[[Категория:Структуры данных]]
[[Категория:Структуры данных]]
[[Категория:Статьи с примерами кода Python]]

Текущая версия на 11:29, 20 октября 2023

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

Применение

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

Объектный пул удобен, если объект владеет другими ресурсами, кроме памяти — например, сетевыми сокетами. Либо если коллекция объектов отнимает значительную часть памяти компьютера и «мусора» создаётся действительно много.

Переполнение

Если в пуле нет ни одного свободного объекта, возможна одна из трёх стратегий:

  1. Расширение пула.
  2. Отказ в создании объекта, аварийная остановка.
  3. В случае многозадачной системы можно подождать, пока один из объектов не освободится.

Примеры

  1. Информация об открытых файлах в DOS.
  2. Информация о видимых объектах во многих компьютерных играх (хорошим примером является движок Doom). Эта информация актуальна только в течение одного кадра; после того, как кадр выведен, список опустошается.
  3. Компьютерная игра для хранения всех объектов на карте, вместо того, чтобы использовать обычные механизмы распределения памяти, может завести массив такого размера, которого заведомо хватит на все объекты, и свободные ячейки держать в виде связного списка. Такая конструкция повышает скорость, уменьшает фрагментацию памяти и снижает нагрузку на сборщик мусора (если он есть).

Пример реализации

Пример на Python

 
#coding: utf-8
"""
Представим ситуацию, что у нас есть корабль, который может выдержать несколько выстрелов.
Создание объекта Shot стоит дорого.
Поэтому объекты семейства Shot создаём 1 раз.
А по истечении жизни объект остаётся в памяти.
"""
class Shot(object):
    """Сущность, способная пережить несколько попаданий"""
    def __init__(self, lifetime=5):
        self.lifetime = lifetime

    def update(self):
        self.lifetime -= 1
        return self.lifetime > 0

class ObjectPool:
    """Пул объектов"""
    def __init__(self, **kwargs):
        """Создание пула"""
        self._clsname = kwargs['classname']
        self._args = kwargs.get('args', [])
        self._num_objects = max(kwargs['num'], 0)
        self._pred = kwargs['update_func']
        self._max_objects = kwargs.get('max', self._num_objects)

        # Create the objects
        self._objs = [apply(self._clsname, self._args)
                      for x in range(self._num_objects)]
        self._end = len(self._objs)

    def _extend_list(self, args):
        """Добавить одно место в пул"""
        self._objs.append(apply(self._clsname, args))
        self._num_objects += 1

    def add(self, *args):
        """Добавить один объект в пул"""
        newend = self._end + 1
        # Если достигнут максимум - отбой
        if newend > self._max_objects:
            return None
        # Если заняли все места - добавляем еще одно место
        if newend > len(self._objs):
            self._extend_list(args)
        else:
            self._objs[self._end].reset(*args)
        self._end += 1
        return self._end - 1

    def update(self, *args):
        """Обновить все объекты в пуле"""
        self._end = partition(self._pred, self._objs, 0, self._end, args)
        return self._end


def update_object(x):
    """Обновить объект"""
    return x.update()


def partition(pred, seq, first, last, *args):
    """Функция сортировки объектов"""
    if first > last:
        return 0

    for i in range(first, last):
        if not pred(seq[i]):
            break
    else:
        return last

    for j in range(i+1, last):
        if pred(seq[j]):
            seq[i], seq[j] = seq[j], seq[i]
            i += 1
    return i

# Собственно использование пула
shots = ObjectPool(classname=Shot, update_func=update_object, num=5)
while shots.update():
    pass

print "Done!"

}}

Пример на C++

#include <vector>

class Object
{
	// ...
};


class ObjectPool
{
	private:
		struct PoolRecord
		{
			Object* instance;
			bool    in_use;
		};

		std::vector<PoolRecord> m_pool;

	public:
		Object* createNewObject()
		{
			for (size_t i = 0; i < m_pool.size(); ++i)
			{
				if (! m_pool[i].in_use)
				{
					m_pool[i].in_use = true; // переводим объект в список используемых
					return m_pool[i].instance;
				}
			}
			// если не нашли свободный объект, то расширяем пул
			PoolRecord record;
			record.instance = new Object;
			record.in_use   = true;

			m_pool.push_back(record);

			return record.instance;
		}

		void deleteObject(Object* object)
		{
			// в реальности не удаляем, а лишь помечаем, что объект свободен
			for (size_t i = 0; i < m_pool.size(); ++i)
			{
				if (m_pool[i].instance == object)
				{
					m_pool[i].in_use = false;
					break;
				}
			}
		}

		virtual ~ObjectPool()
		{
			// теперь уже "по-настоящему" удаляем объекты
			for (size_t i = 0; i < m_pool.size(); ++i)
				delete m_pool[i].instance;
		}
};


int main()
{
	ObjectPool pool;
	for (size_t i = 0; i < 1000; ++i)
	{
		Object* object = pool.createNewObject();
		// ...
		pool.deleteObject(object);
	}
	return 0;
}

Ссылки

Шаблон:Типы Паттернов