简体   繁体   English

Python 多线程程序给出了意外的 output

[英]Python multithreading program is giving unexpected output

I know that there is no guarantee regarding the order of execution for the threads.我知道不能保证线程的执行顺序。 But my doubt is when I ran below code,但我的疑问是当我运行以下代码时,

import threading

def doSomething():
    print("Hello ")

d = threading.Thread(target=doSomething, args=())
d.start()
print("done")

Output that is coming is either即将到来的 Output 要么

Hello done

or this或这个

Hello 
done

May be if I try too much then it might give me below as well可能是如果我尝试太多,那么它也可能给我下面

done
Hello

But I am not convinced with the first output.但我不相信第一个 output。 Since order can be different but how come both outputs are available in the same line.由于顺序可能不同,但是为什么两个输出都在同一行中可用。 Does that means that one thread is messing up with other threads working?这是否意味着一个线程正在与其他线程一起工作?

This is a classic race condition.这是一个经典的比赛条件。 I can't personally reproduce it, and it would likely vary by interpreter implementation and the precise configuration applied to stdout .我无法亲自复制它,它可能会因解释器实现和应用于stdout的精确配置而异。 On Python interpreters without a GIL, there is basically no protection against races, and this behavior is expected to a certain extent.在没有 GIL 的 Python 解释器上,基本上没有针对种族的保护,这种行为在一定程度上是可以预料的。 Python interpreters do tend to try to protect you from egregious data corruption due to threading, unlike C/C++, but even if they ensure every byte written ends up actually printed, they usually wouldn't try to make explicit guarantees against interleaving;与 C/C++ 不同,Python 解释器确实倾向于保护您免受由于线程导致的严重数据损坏,但即使它们确保写入的每个字节最终都实际打印出来,它们通常也不会尝试明确保证不会发生交错; Hdelolnoe would be a possible (if fairly unlikely given likely implementations) output when you're making no effort whatsoever to synchronize access to stdout .当您不努力同步对stdout的访问时, Hdelolnoe将是可能的(如果考虑到可能的实现,则不太可能) output 。

On CPython, the GIL protects you more, and writing a single string to stdout is more likely to be atomic, but you're not writing a single string.在 CPython 上,GIL 为您提供更多保护,将单个字符串写入stdout更有可能是原子的,但您不是在编写单个字符串。 Essentially, the implementation of print is to write objects one by one to the output file object as it goes, it doesn't batch up to a single string then call write just once.本质上, print的实现是将对象一个一个地写入 output 文件 object ,它不会批量处理单个字符串,然后只调用一次write What this means is that:这意味着:

print("Hello ")  # Implicitly outputs default end argument of '\n' after printing provided args

is roughly equivalent to:大致相当于:

sys.stdout.write("Hello ")
sys.stdout.write("\n")

If the underlying stack of file objects that implements sys.stdout decides to engage in real I/O in response to the first write , they'll release the GIL before performing the actual write, allowing the main thread to catch up and potentially grab the GIL before the worker thread is given a chance to write the newline.如果实现sys.stdout的底层文件对象堆栈决定参与真正的 I/O 以响应第一次write ,它们将在执行实际写入之前释放 GIL,从而允许主线程赶上并可能抢占GIL 在工作线程有机会写入换行符之前。 The main thread then outputs the done and then the newlines from each print come out in some unspecified (and irrelevant) order based on further potential races.然后主线程输出done ,然后每个print的换行符根据进一步的潜在比赛以某种未指定(和不相关)的顺序出现。

Assuming you're on CPython, you could probably fix this by changing the code to this equivalent code using single write calls:假设您使用的是 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")

and you'd be back to a race condition that only swaps the order, without interleaving (the language spec wouldn't guarantee a thing, but most reasonable implementations would be atomic for this case).并且您将回到仅交换顺序而没有交错的竞争条件(语言规范不能保证任何事情,但对于这种情况,最合理的实现将是原子的)。 If you want it to work with any guarantees without relying on the quirks of the implementation, you have to synchronize:如果您希望它在不依赖实现的怪癖的情况下使用任何保证,则必须同步:

import threading

lck = threading.Lock()

def doSomething():
    with lck:
        print("Hello ")

d = threading.Thread(target=doSomething)
d.start()
with lck:
    print("done")

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM