[英]Trying to understand python multithreading
请考虑以下代码:
import threading
def printer():
for i in range(2):
with lock:
print ['foo', 'bar', 'baz']
def main():
global lock
lock = threading.Lock()
threads = [threading.Thread(target=printer) for x in xrange(2)]
for t in threads:
t.start()
t.join()
main()
我可以理解这段代码,这很清楚:我们创建了两个线程,然后按顺序运行它们-仅在第一个线程完成时才运行第二个线程。 好的,现在考虑另一个变体:
import threading
def printer():
for i in range(2):
with lock:
print ['foo', 'bar', 'baz']
def main():
global lock
lock = threading.Lock()
threads = [threading.Thread(target=printer) for x in xrange(2)]
for t in threads:
t.start()
for t in threads:
t.join()
main()
这里会发生什么? 好的,我们并行运行它们,但是让主线程在第二个变体中等待子线程的目的是什么? 它如何影响输出?
在第二种变体中,执行顺序的定义要少得多。 每次通过打印机中的循环释放锁定。 在这两种变体中,一个线程中都有两个线程和两个循环。 在第一个变体中,由于一次只运行一个线程,因此您知道总排序。 在第二个变体中,每次释放锁定时,线程运行可能会更改。 所以你可能会得到
或者*线程2循环1 *线程1循环1 *线程1循环2 *线程2循环2
唯一的限制是给定线程中的loop1在该线程中的循环2之前运行,并且两个print语句放在一起,因为这两个语句均被锁定。
在这种特殊情况下,我不确定第二个变体中对t.join()
的调用是否具有可观察到的效果。 它保证了主线程将是最后一个结束的线程,但是我不确定在此代码中您能否以任何方式观察到这一点。 在更复杂的代码中,加入线程可能很重要,因此仅在所有线程终止后才执行清除操作。 如果您有守护程序线程,这也可能非常重要,因为当所有非守护程序线程终止时,整个程序将终止。
为了更好地理解python中的多线程,您需要首先了解main
线程和children
线程之间的关系。
main
线程是程序的入口,它是由系统在运行脚本时创建的。 例如,在您的脚本中, main
函数在main
线程中运行。
当您实例化Thread
类时, children
线程是由您的main
线程创建的。
最重要的是主线程如何控制子线程。 基本上, Thread
实例是main
线程了解并控制该子线程的所有内容。 在创建子线程时,该子线程不会立即运行,直到该线程实例上的主线程调用start
函数为止。 启动子线程之后,您可以假定main
线程和child
线程现在正在并行运行。
但是更重要的一件事是main
线程如何知道child
线程的任务已完成。 虽然main
线程一无所知任务如何通过做child
的线程,它不知道的运行状态child
线程。 Thread.is_alive
可以通过main
线程检查Thread.is_alive
的状态。 在实践中,始终使用Thread.join
函数告诉main
线程等待,直到child
线程完成。 该函数将阻塞main
线程。
好的,让我们检查一下您困惑的两个脚本。 对于第一个脚本:
for t in threads:
t.start()
t.join()
循环中的children
线程start
,然后一个接一个地join
。 请注意, start
不会阻塞main
线程,而join
会阻塞main
线程,直到该child
线程完成。 因此,它们按顺序运行。
而对于第二个脚本:
for t in threads:
t.start()
for t in threads:
t.join()
所有children
线程均在第一个循环中启动。 由于Thread.start
函数不会阻塞main
线程,因此所有children
线程在第一个循环之后并行运行。 在第二个循环中, main
线程将逐个等待每个child
线程完成的任务。
现在,我认为您应该注意到这两个脚本之间的区别:在第一个脚本中, children
线程一个接一个地运行,而在第二个脚本中,它们同时运行。
python线程还有其他有用的主题:
(1)如何处理键盘中断异常,例如,当我想通过Ctrl-C
终止程序时? 只有main
线程会收到异常,您必须处理children
线程的终止。
(2)多线程与多处理。 尽管我们说线程是并行的,但在CPU级别并不是真正的并行。 因此,如果您的应用程序占用大量CPU,请尝试进行多处理,如果您的应用程序占用大量I / O,则多线程可能就足够了。
顺便说一句,请仔细阅读python线程部分的文档并尝试一些代码可以帮助您理解它。
希望这会有所帮助。 谢谢。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.