How does multi-threading work with Ice for Python?
Python supports multi-threaded programming, but the interpreter is inherently single threaded: a global interpreter lock (GIL) allows only one thread at a time to execute a Python opcode. Despite this limitation, careful management of the GIL can still provide performance improvements; for example, a thread that is about to block on I/O can release the GIL so a different thread can use the CPU in the meantime. Although the GIL is an implementation detail that is typically of interest only to developers of Python extensions, it is still important for Python programmers to understand the semantics of the GIL and how it affects their applications.
The Ice extension for Python is built as a run-time layer on top of Ice for C++, which is threaded. Depending on its thread pool configuration, the Ice for C++ run time may have several native threads running concurrently in the background, performing tasks such as accepting a new connection or reading an incoming request. The Python interpreter is unaware of these threads; furthermore, they are not constrained by the GIL. It is only when an Ice thread needs to call into the Python API, for example to dispatch an operation to a Python servant, that it must acquire the GIL.
The GIL also affects calls from Python code into the Ice API, such as when a program invokes a remote operation via a proxy. In this scenario, the GIL is already locked by the interpreter and the Ice extension releases the GIL to give another thread a chance to execute while the remote operation is in progress. If the GIL were to remain locked for the duration of the remote operation, not only would we unnecessarily restrict concurrency, but we would also introduce the possibility of a deadlock. For example, the operation might invoke a callback to a servant in the client process; if the GIL were to remain locked during the initial operation, the Ice extension would be unable to acquire the GIL before dispatching the callback. For these reasons, the Ice extension releases the GIL during any Ice API method that can block the calling thread, and acquires the GIL again just prior to returning control to the interpreter.
In practice, the limitations of the GIL mean that only one thread executes in the servant implementation code of an Ice server that is written in Python. Note that the single-threaded nature of the Python interpreter does not guarantee that all servant operations will execute atomically. In particular, if a Python server has more than one thread in its thread pool, it is possible for one thread to enter an operation on a servant, get suspended, and then for another thread to execute part or all of an operation on the same or a different servant. Proper use of synchronization is required in this case to protect shared resources. If you want true single-threaded execution, such that every operation runs to completion before a thread enters another operation, you must leave the Ice.ThreadPool.Server.Size
property at its default setting of 1.
If you find that you cannot tolerate the performance limitations of serialized execution in your Ice server, currently the only option is to implement the server in a language other than Python, at least until Python provides true multi-threading support.