C++ In
C++26, the
C++ Standard Library introduces a new header with the data structure std::hive, which essentially implements an object pool. It is a collection that reuses erased elements' memory. Along with it is a class std::hive_limits for layout information on block capacity limits. It is itself based on the class plf::colony from the plf library. import std; using std::hive; using std::plus; using std::ranges::iota_view; int main(int argc, char* argv[]) { hive intHive; // Insert 100 ints: for (int i: iota_view(0, 100)) { intHive.insert(i); } // Erase half of them: for (int i: intHive) { intHive.erase(i); } int total = std::ranges::fold_left(intHive, 0, plus()); std::println("Total of all elements: {}", total); return 0; }
C# In the .NET
Base Class Library there are a few objects that implement this pattern. System.Threading.ThreadPool is configured to have a predefined number of threads to allocate. When the threads are returned, they are available for another computation. Thus, one can use threads without paying the cost of creation and disposal of threads. The following shows the basic code of the object pool design pattern implemented using C#. Pool is shown as a static class, as it's unusual for multiple pools to be required. However, it's equally acceptable to use instance classes for object pools. namespace Wikipedia.Examples; using System; using System.Collections.Generic; // 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 _available = new(); private static List _inUse = new(); 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(); _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; } } In the code above, the PooledObject has properties for the time it was created, and another, that can be modified by the client, that is reset when the PooledObject is released back to the pool. Shown is the clean-up process, on release of an object, ensuring it is in a valid state before it can be requested from the pool again.
Go The following Go code initializes a resource pool of a specified size (concurrent initialization) to avoid resource race issues through channels, and in the case of an empty pool, sets timeout processing to prevent clients from waiting too long. // 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
Java Java supports
thread pooling via java.util.concurrent.ExecutorService and other related classes. The executor service has a certain number of "basic" threads that are never discarded. If all threads are busy, the service allocates the allowed number of extra threads that are later discarded if not used for the certain expiration time. If no more threads are allowed, the tasks can be placed in the queue. Finally, if this queue may get too long, it can be configured to suspend the requesting thread. In : package org.wikipedia.examples; public class PooledObject { private String temp1; private String temp2; private 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; } } In : package org.wikipedia.examples; import java.util.HashMap; import java.util.Map; public class PooledObjectPool { public static final long EXPIRY_TIME = 6000; // 6 seconds private static Map available = new HashMap<>(); private static Map inUse = new HashMap<>(); public synchronized static PooledObject getObject() { long now = System.currentTimeMillis(); if (!available.isEmpty()) { for (Map.Entry entry : available.entrySet()) { if (now - entry.getValue() > EXPIRY_TIME) { // 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 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 map) { Map.Entry entry = map.entrySet().iterator().next(); PooledObject key = entry.getKey(); // Long value = entry.getValue(); map.remove(entry.getKey()); return key; } private static PooledObject popElement(HashMap map, PooledObject key) { map.remove(key); return key; } public static void cleanUp(PooledObject po) { po.setTemp1(null); po.setTemp2(null); po.setTemp3(null); } }
Rust There is a
Rust crate called
colony which is based on std::hive (or plf::colony) from C++. It has no longer been maintained since January 2024. use colony::Colony; fn main() { let mut colony: Colony = Colony::new(); // Insert let foo_handle = colony.insert("foo"); let bar_handle = colony.insert("bar"); // Remove assert_eq!(colony.remove(foo_handle), Some("foo")); // Lookup assert_eq!(colony.get(foo_handle), None); assert_eq!(colony.get(bar_handle), Some(&"bar")); // Iteration for (key, &value) in colony.iter() { assert_eq!((key, value), (bar_handle, "bar")); } } == See also ==