2

I was going through the ThreadPool pattern and it resembled the Object Pool pattern. The only difference that I can think of is that most explanation about the ThreadPool explicitly mentioned the use of task queue while in the Object Pool pattern is not explicitly stated. Can someone please explain to me the difference between these two patterns?

3

Yes, these patterns are very similar, but they're also different in very crucial ways.

With object pool, all you do is check out an object for your usage and you do whatever you want to do with that object. Object pool is used as a way to borrow ownership/control of an object for a duration of execution. An object pool is typically used when the creation of that object is expensive, awkward, or impossible because the objects are connected to a unique resource. For example a Continuous Integration system may have multiple copies of the build context (i.e. filesystem folder or a docker container), creating new build context is quite expensive so a pool is used to manage access to the build context. Another example is creating new TCP/TLS connection requires handshakes which involves multiple roundtrips to a server that's probably half way around the world, so a connection pool may be used to reuse connections. Another example may be the use of pool to limit usage of a coprocessor (e.g. GPU), a physical machine may only have access to a limited number of physical GPU, so a pool may be used because you can't just create a new GPU out of thin air.

Thread pooling is a kind of an object pool, and internally a thread pool likely may contain use an object pool, however when people talk about thread pool, usually they aren't really talking about the underlying object pool, but rather thread pool is really just a particular type of implementation of the Executor pattern. The difference between typical object pool and thread pool is that with a thread pool, rather than checking out an object, you submit a task to be completed asynchronously and in parallel. The thread pool will execute that task in the pool of worker threads that waits on the pool's task queue.

3

The difference between a Thread Pool and an Object Pool is the same as the difference between a Toyota Corolla and a Vehicle.

An Object Pool is a pool of objects. A Thread Pool is a pool of objects, but not any kind of object, only threads.

They both serve the same basic purpose: objects that are expensive to create, expensive to destroy, limited in number, or any combination thereof, are stored instead of destroyed when they are no longer needed, and re-used instead of created when a "new" one is needed.

Note that in the general case, Object Pool is an anti-pattern. Objects shouldn't be expensive. If they are, then there is something wrong with your runtime environment. In an OO language, you should not need not to think about whether you can afford creating an object or not. Objects are the basis of OO, if your runtime can't handle a couple million objects, get a better one.

Likewise, in multi-threaded programming, threads are the basic building blocks, and your runtime should not restrict you in how you are using them. For example, on the BEAM/HiPE VM, you can create 10 million processes (note that processes are much more expensive than threads; threads basically only have an instruction pointer, a stack, and some limited thread-local storage, processes have their own heap, their own protected memory, and on BEAM/HiPE even their own garbage collector) on a 15 year old 32-bit laptop without even breaking a sweat, so it's not like it is impossible to make threads cheap.

However, we live in the real world, and there are some runtime environments where threads are so expensive that re-using them is a necessity. For example, on Linux, a thread weighs 4 KiByte or 8 KiByte compared to a BEAM/HiPE process, which is only ~300 Byte. And that's where the Thread Pool comes in.

We create a certain number of threads (typically on the same order as we have CPU cores for computation plus a couple of ones for I/O). And then we create a lightweight abstraction (let's call it a CodeSnippet) that is basically the same thing as a thread or a process, but lightweight, and we can create as many of those as we want. When we want to execute one of those code snippets, we pull a thread from our pool, run it, grab the result, dispose the code snippet (remember, it's lightweight, so creating and destroying them is no problem), and put the thread back in the pool. That way, we can have a lightweight abstraction of a thread-like thing mapped to actual threads without having to create a large number of threads or restrict ourselves to only using a small number of concurrent objects.

  • 1
    Erlang processes are not cheap, it's just incorrectly named. Other languages calls Erlang processes green threads, fibers, or coroutine instead of confusing people with incorrect terminology. Real OS processes is not just a execution context, it's also security context, memory spaces, have real preemption (based on machine interrupts), and provides a bunch of other system services that real programs needs and expects. Erlang processes are only cheap because it isn't a process at all, it's not intended to be a security context. – Lie Ryan Nov 13 '18 at 11:15
  • So, you don't consider MacOS processes "real" according to your definition of "real process"? What about Singularity OS's processes, are they "real" according to your definition? And OS/400's? What makes an OS/400 process "real" and a BEAM/HiPE process not? – Jörg W Mittag Nov 13 '18 at 19:46
  • Threads, Fibers, and Coroutines don't have memory protection, Processes do, so BEAM/HiPE Processes are definitely not Threads, Fibers, or Coroutines. – Jörg W Mittag Nov 13 '18 at 20:06
  • This is exactly the type of confusion that Erlang is propagating. Beam/HiPE process don't actually have memory protection, it's really just not sharing a global namespace. Pretty much any memory safe, functional languages don't either; under Erlang's definition of a process, all functional languages are creating new processes every time you call a new function. – Lie Ryan Nov 14 '18 at 7:55
  • A process is not just about memory protection, real OS process understands multi users, and the process permission system define how it interacts with shared resources like filesystems, peripheral devices, etc according to the user's privilege. Erlang's processes does nothing of that sort, it is nothing more than an execution context not unlike coroutines. – Lie Ryan Nov 14 '18 at 7:55
1

A thread pool is a kind of object pool, except that the threads involved in pooling hold an operating system resource (whether temporarily or permanently).

This means that there could — in theory — be an implementation of thread pooling that does not pool (language) objects and instead creates a new (language) thread object with an existing operating system thread from the thread pool.  In such an implementation we would still acknowledge thread pooling but not object pooling.

The management of the object in an object pool and thread pool may differ as well, as the thread pool objects may be managed in a highly controlled way and with respect to specific operating system interactions, whereas an object pool perhaps managed more simply.

The thread pool is specifically intended for the purpose of more efficient task execution, whereas the object pool design pattern is more general.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.