Python 多线程程序给出了意外的输出
Python multithreading program is giving unexpected output
我知道没有关于线程执行顺序的gua运行tee。但我的疑问是当我运行下面的代码时,
import threading
def doSomething():
print("Hello ")
d = threading.Thread(target=doSomething, args=())
d.start()
print("done")
即将到来的输出是
Hello done
或这个
Hello
done
可能是如果我尝试太多,它可能也会给我下面的结果
done
Hello
但我不相信第一个输出。由于顺序可能不同,但为什么两个输出都在同一行中可用。这是否意味着一个线程干扰了其他线程的工作?
这是一个典型的竞争条件。我无法亲自重现它,它可能会因解释器实现和应用于 stdout
的精确配置而异。在 Python 没有 GIL 的解释器上,基本上没有对种族的保护,这种行为在一定程度上是意料之中的。 Python 与 C/C++ 不同,解释器确实倾向于保护您免受由于线程引起的严重数据损坏,但即使他们确保写入的每个字节最终都实际打印出来,他们通常也不会尝试明确保证不交错; Hdelolnoe
当您不做任何努力来同步对 stdout
.
的访问时,可能(如果给定可能的实现不太可能)输出
在 CPython 上,GIL 对您的保护更多,将单个字符串写入 stdout
更有可能是原子的,但您并不是在写入单个字符串。本质上,print
的实现是将对象一个一个地写入输出文件对象,它不会批处理到单个字符串然后只调用一次 write
。这意味着:
print("Hello ") # Implicitly outputs default end argument of '\n' after printing provided args
大致相当于:
sys.stdout.write("Hello ")
sys.stdout.write("\n")
如果实现 sys.stdout
的底层文件对象堆栈决定参与真正的 I/O 以响应第一个 write
,他们将在执行实际之前释放 GIL写入,允许主线程赶上并可能在工作线程有机会写入换行符之前获取 GIL。然后主线程输出 done
,然后每个 print
的换行符根据进一步的潜在竞争以某种未指定(且不相关)的顺序出现。
假设您使用的是 CPython,您可能可以通过使用单个 write
调用将代码更改为等效代码来解决此问题:
import threading
import sys
def doSomething():
sys.stdout.write("Hello \n")
d = threading.Thread(target=doSomething) # If it takes no arguments, no need to pass args
d.start()
sys.stdout.write("done\n")
并且您将回到只交换顺序而不交错的竞争条件(语言规范不能保证任何事情,但对于这种情况,大多数合理的实现都是原子的)。如果你想让它在不依赖于实现的怪癖的情况下以任何保证工作,你必须同步:
import threading
lck = threading.Lock()
def doSomething():
with lck:
print("Hello ")
d = threading.Thread(target=doSomething)
d.start()
with lck:
print("done")
我知道没有关于线程执行顺序的gua运行tee。但我的疑问是当我运行下面的代码时,
import threading
def doSomething():
print("Hello ")
d = threading.Thread(target=doSomething, args=())
d.start()
print("done")
即将到来的输出是
Hello done
或这个
Hello
done
可能是如果我尝试太多,它可能也会给我下面的结果
done
Hello
但我不相信第一个输出。由于顺序可能不同,但为什么两个输出都在同一行中可用。这是否意味着一个线程干扰了其他线程的工作?
这是一个典型的竞争条件。我无法亲自重现它,它可能会因解释器实现和应用于 stdout
的精确配置而异。在 Python 没有 GIL 的解释器上,基本上没有对种族的保护,这种行为在一定程度上是意料之中的。 Python 与 C/C++ 不同,解释器确实倾向于保护您免受由于线程引起的严重数据损坏,但即使他们确保写入的每个字节最终都实际打印出来,他们通常也不会尝试明确保证不交错; Hdelolnoe
当您不做任何努力来同步对 stdout
.
在 CPython 上,GIL 对您的保护更多,将单个字符串写入 stdout
更有可能是原子的,但您并不是在写入单个字符串。本质上,print
的实现是将对象一个一个地写入输出文件对象,它不会批处理到单个字符串然后只调用一次 write
。这意味着:
print("Hello ") # Implicitly outputs default end argument of '\n' after printing provided args
大致相当于:
sys.stdout.write("Hello ")
sys.stdout.write("\n")
如果实现 sys.stdout
的底层文件对象堆栈决定参与真正的 I/O 以响应第一个 write
,他们将在执行实际之前释放 GIL写入,允许主线程赶上并可能在工作线程有机会写入换行符之前获取 GIL。然后主线程输出 done
,然后每个 print
的换行符根据进一步的潜在竞争以某种未指定(且不相关)的顺序出现。
假设您使用的是 CPython,您可能可以通过使用单个 write
调用将代码更改为等效代码来解决此问题:
import threading
import sys
def doSomething():
sys.stdout.write("Hello \n")
d = threading.Thread(target=doSomething) # If it takes no arguments, no need to pass args
d.start()
sys.stdout.write("done\n")
并且您将回到只交换顺序而不交错的竞争条件(语言规范不能保证任何事情,但对于这种情况,大多数合理的实现都是原子的)。如果你想让它在不依赖于实现的怪癖的情况下以任何保证工作,你必须同步:
import threading
lck = threading.Lock()
def doSomething():
with lck:
print("Hello ")
d = threading.Thread(target=doSomething)
d.start()
with lck:
print("done")