Threading support
Keywords: manual

Subsections

- Tk issues
- Signals and Threads
- I/O pitfalls

#
Threading support

WARNING: The threading support is still somewhat experimental, and it has only seen reasonable testing under Linux. Threaded code is particularly tricky to debug, and it tends to show extremely platform-dependent behavior. Since I only have access to Linux machines, I will have to rely on user's experiences and assistance for this area of IPython to improve under other platforms.

IPython, via the `-gthread` , `-qthread` and `-wthread` options (described in Sec. 5.1), can run in multithreaded mode to support pyGTK, Qt and WXPython applications respectively. These GUI toolkits need to control the python main loop of execution, so under a normal Python interpreter, starting a pyGTK, Qt or WXPython application will immediately freeze the shell.

IPython, with one of these options (you can only use one at a time), separates the graphical loop and IPython's code execution run into different threads. This allows you to test interactively (with `%run`, for example) your GUI code without blocking.

A nice mini-tutorial on using IPython along with the Qt Designer application is available at the SciPy wiki: `http://www.scipy.org/wikis/topical_software/QtWithIPythonAndDesigner`.

## Tk issues

As indicated in Sec. 5.1, a special `-tk` option is provided to try and allow Tk graphical applications to coexist interactively with WX, Qt or GTK ones. Whether this works at all, however, is very platform and configuration dependent. Please experiment with simple test cases before committing to using this combination of Tk and GTK/Qt/WX threading in a production environment.

## Signals and Threads

When any of the thread systems (GTK, Qt or WX) are active, either directly or via `-pylab` with a threaded backend, it is impossible to interrupt long-running Python code via `Ctrl-C`. IPython can not pass the KeyboardInterrupt exception (or the underlying `SIGINT`) across threads, so any long-running process started from IPython will run to completion, or will have to be killed via an external (OS-based) mechanism.

To the best of my knowledge, this limitation is imposed by the Python interpreter itself, and it comes from the difficulty of writing portable signal/threaded code. If any user is an expert on this topic and can suggest a better solution, I would love to hear about it. In the IPython sources, look at the `Shell.py` module, and in particular at the `runcode()` method.

## I/O pitfalls

Be mindful that the Python interpreter switches between threads every bytecodes, where the default value as of Python 2.3 is This value can be read by using the `sys.getcheckinterval()` function, and it can be reset via `sys.setcheckinterval(N)`. This switching of threads can cause subtly confusing effects if one of your threads is doing file I/O. In text mode, most systems only flush file buffers when they encounter a ``\n'`. An instruction as simple as
`print >> filehandle, ``hello world''`
actually consists of several bytecodes, so it is possible that the newline does not reach your file before the next thread switch. Similarly, if you are writing to a file in binary mode, the file won't be flushed until the buffer fills, and your other thread may see apparently truncated files.

For this reason, if you are using IPython's thread support and have (for example) a GUI application which will read data generated by files written to from the IPython thread, the safest approach is to open all of your files in unbuffered mode (the third argument to the `file/open` function is the buffering value):
`filehandle = open(filename,mode,0)`

This is obviously a brute force way of avoiding race conditions with the file buffering. If you want to do it cleanly, and you have a resource which is being shared by the interactive IPython loop and your GUI thread, you should really handle it with thread locking and syncrhonization properties. The Python documentation discusses these.