线程似乎按顺序运行线程

threading appears to run threads sequentially

提问人:MAK 提问时间:11/9/2009 最后编辑:martineauMAK 更新时间:8/25/2022 访问量:14831

问:

我正在尝试在我正在处理的 Python 项目中使用线程,但线程在我的代码中似乎没有按照预期的方式运行。似乎所有线程都是按顺序运行的(即线程 2 在线程 1 结束之后启动,它们不会同时启动)。我写了一个简单的脚本来测试这一点,它也按顺序运行线程。

import threading

def something():
    for i in xrange(10):
        print "Hello"

def my_thing():
    for i in xrange(10):
        print "world"   

threading.Thread(target=something).start()
threading.Thread(target=my_thing).start() 

这是我从运行它中获得的输出:

Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
world
world
world
world
world
world
world
world
world
world

在循环的迭代次数要多得多的情况下,也会观察到相同的行为。

我尝试在网上搜索较旧的 SO 答案,但我找不到任何有帮助的东西。 有人可以指出这段代码有什么问题吗?

Python 多线程

评论


答:

11赞 Jochen Ritzel 11/9/2009 #1

在第二个线程启动第一个线程所需的时间内,已经循环和打印。

这里看起来像这样,你可以看到第二个线程在第一个线程发出几个问候后开始。

Hello
Hello
Hello
Hello
Hello
Helloworld

Helloworld

Helloworld

Helloworld

Helloworld

world
world
world
world
world

顺便说一句:你的例子根本没有意义。线程的唯一原因是 IO,而 IO 很慢。当您添加一些睡眠来模拟 IO 时,它应该按预期工作:

import threading
from time import sleep

def something():
    for i in xrange(10):
        sleep(0.01)
        print "Hello"

def my_thing():
    for i in xrange(10):
        sleep(0.01)
        print "world"

threading.Thread(target=something).start()
threading.Thread(target=my_thing).start()

出现一个狂野的混合:

worldHello

Helloworld

Helloworld

worldHello

Helloworld

Helloworld

worldHello

Helloworld

worldHello

Helloworld

评论

2赞 MAK 11/9/2009
即使 for 循环的迭代次数大大增加/减少,我也没有得到这样的输出。在我的计算机上,它始终是连续的。正如 abyx 所建议的那样,我认为这取决于操作系统/处理器。
0赞 MAK 11/10/2009
正如我在问题中所说,这只是我问题的一个例子,而不是我正在使用的代码(它要大得多)。在我的实际代码中,其中一个线程运行一个循环来侦听 dbus 信号。
3赞 abyx 11/9/2009 #2

这实际上取决于您的操作系统的调度程序,即您的处理器。
除此之外,众所周知,由于 GIL(PDF),CPython 的线程并不完美,简而言之,这意味着很多时候线程确实是按顺序运行的,或者类似的东西。

评论

2赞 Eric O. Lebigot 11/9/2009
您可能的意思是 CPython 线程受到 GIL...比如说,Jython 中没有 GIL。
15赞 viraptor 11/9/2009 #3

目前在 python 中,线程在执行一定数量的字节码指令后会发生变化。它们不会同时运行。只有当其中一个线程调用一些 I/O 密集型或不影响 python 的模块时,您才会有线程并行执行,这些模块可以释放 GIL(全局解释器锁)。

我很确定,如果你将循环数提高到 10000 左右,你会把输出弄混。请记住,简单地生成第二个线程也需要“很多”时间。

评论

0赞 MAK 11/9/2009
10000 次迭代的相同行为
0赞 MAK 11/9/2009
在我正在处理的实际项目中,其中一个线程是一个无限循环,它侦听消息并在消息到达时调用回调函数。它只是阻止所有其他线程。不幸的是,实际的循环代码无法修改(我只是调用线程中一个类的 run() 方法)。
0赞 viraptor 11/9/2009
当我像这样运行脚本时: 我得到: 8969 你好 |1 你好世界 |6626 世界 |1 |3373 世界 |1030 你好。所以它确实改变了控制 - 只是不那么频繁......./pythr.py | uniq -c
4赞 viraptor 11/9/2009
解决此问题的另一种方法是使用模块的 instread。这样你的代码实际上就可以并行运行。multiprocessingthreading
1赞 MAK 11/10/2009
谢谢。多处理解决了我的项目代码中的问题。
4赞 Dave Kirby 11/9/2009 #4

行为也可能根据系统使用的是单个处理器还是多个处理器而改变,正如 David Beazley 的演讲所解释的那样。

正如 viraptor 所说,第一个线程将在执行 sys.getcheckinterval() 字节码(默认为 100)后释放 GIL。粗略地总结一下 David Beazley 所说的话,在单个处理器系统上,第二个线程将有机会接管。但是,在多核系统上,第二个线程可能在不同的内核上运行,第一个线程将尝试重新获取锁,并且可能会成功,因为操作系统没有时间切换处理器。这意味着,在具有 CPU 密集线程的多核系统上,其他线程可能永远无法查看。

解决此问题的方法是向两个循环添加一个 sleep 语句,以便它们不再受 CPU 限制。

1赞 sospeter mule 8/25/2022 #5

通常,秘诀是始终确保在启动线程时使用以下语法。

Note threading.Thread(target=threadSet1).start() the function threadSet1 has not brackets at the end.

import threading
from time import sleep

def spawn(num, typex):
    import time
    start = time.time()
    print(num, typex)

def threadSet1():
    for i in range(1000):
        sleep(0.01)
        t = threading.Thread(target=spawn(i, "world"))
    t.daemon = True
    t.start()
def threadSet2():
    for i in range(1000):
        sleep(0.01)
        t = threading.Thread(target=spawn(i, "hello"))
        t.daemon = True
        t.start()
if __name__ == '__main__':
    threading.Thread(target=threadSet1).start()
    threading.Thread(target=threadSet2).start()

不要陷入使用以下语法的陷阱,因为它会串行线程地执行它们。线程(target=threadSet1()).start() 线程。线程(target=threadSet2()).start()Note the functions have brackets at the end.