12

I've seen on various mailing lists and forums that people keep mentioning that the print function in Python 3 is thread safe. From my own testing, I see no reason to doubt that.

import threading
import time
import random

def worker(letter):
    print(letter * 50)


threads = [threading.Thread(target=worker, args=(let,)) for let in "ABCDEFGHIJ"]
for t in threads:
    t.start()
for t in threads:
    t.join()

When I run it with Python 3, even though some of the lines may be out of order, they are still always on their own lines. With Python 2, however, the output is fairly sporadic. Some lines are joined together or indented. This is also the case when I from __future__ import print_function

I'm just trying to understand WHY this is the case?

Goodies
  • 4,439
  • 3
  • 31
  • 57
  • Um... There were changes made in the print() functions between 2.7 and 3.6 that made the code thread-safe when it wasn't before. The Python source code is available for both mentioned versions; you're always free to download it and compare the two implementations to see how they differ. – Ken White Mar 17 '17 at 23:19
  • I did that (diffed them) but didn't see anything terribly noticeable. I'm not as familiar with the python API as others are. – Goodies Mar 17 '17 at 23:20
  • 1
    Python 3 includes a complete rewrite of the I/O library; they must have added internal locking as part of that. You are unlikely to be able to pull out a specific change that had this effect. – zwol Apr 16 '17 at 15:49

1 Answers1

5

For Python 3.7: The print() function is a builtin, it by default sends output to sys.stdout, the documentation of which says, among other things:

When interactive, stdout and stderr streams are line-buffered. Otherwise, they are block-buffered like regular text files. You can override this value with the -u command-line option.

So its really the combination of interactive mode and sys.stderr that is responsible for the behaviour of the print function as demonstrated in the example.

And we can get closer to the truth if the worker function in your example program is changed to

def worker(letter):
    print(letter*25, letter*25, sep='\n')

then we get outputs similar to the one below, which clearly shows that print in itself is not thread safe, what you can expect is that individual lines do not get interleaved with each other.

DDDDDDDDDDDDDDDDDDDDDDDDDJJJJJJJJJJJJJJJJJJJJJJJJJ

JJJJJJJJJJJJJJJJJJJJJJJJJ
DDDDDDDDDDDDDDDDDDDDDDDDDGGGGGGGGGGGGGGGGGGGGGGGGG
GGGGGGGGGGGGGGGGGGGGGGGGGAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAHHHHHHHHHHHHHHHHHHHHHHHHH
HHHHHHHHHHHHHHHHHHHHHHHHH


FFFFFFFFFFFFFFFFFFFFFFFFF
IIIIIIIIIIIIIIIIIIIIIIIIICCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCC
IIIIIIIIIIIIIIIIIIIIIIIII

EEEEEEEEEEEEEEEEEEEEEEEEE

EEEEEEEEEEEEEEEEEEEEEEEEEFFFFFFFFFFFFFFFFFFFFFFFFF

BBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBB

So ultimately thread safety of print is determined by the buffering strategy used.

xxa
  • 1,258
  • 9
  • 20