Thread().join 在以下情况下如何工作?
How does Thread().join work in the following case?
我在线程教程中看到如下代码:
from time import sleep, perf_counter
from threading import Thread
start = perf_counter()
def foo():
sleep(5)
threads = []
for i in range(100):
t = Thread(target=foo,)
t.start()
threads.append(t)
for i in threads:
i.join()
end = perf_counter()
print(f'Took {end - start}')
当我 运行 它打印 Took 5.014557975
。好的,那部分没问题。它不像非线程版本那样需要 500 秒。
我不明白 .join
是如何工作的。我注意到没有调用 .join
我得到了 Took 0.007060926999999995
这表明主线程在子线程之前结束。由于 '.join()' 应该阻塞,当循环的第一次迭代发生时,它不会被阻塞并且必须等待 5 秒直到第二次迭代吗?它是如何做到 运行 的?
我一直在阅读 python 线程并不是真正的多线程,它看起来只是(运行s 在单核上),但如果是这样,那么后台时间究竟是多少运行如果不平行就宁?
So '.join()' is supposed to block, so when the first iteration of the loop occurs wont it be blocked and it has to wait 5 seconds till the second iteration?
记住所有的线程都是同时启动的,所有线程都需要 ~5s。
第二个 for
循环等待所有线程完成。第一个线程完成大约需要 5 秒,但其余 99 个线程将大致同时完成,循环的其余 99 次迭代也是如此。
当您在第二个线程上调用 join()
时,它要么已经完成,要么将在几毫秒内完成。
I keep reading python threading is not truly multithreaded and it only appears to be (runs on a single core), but if that is the case then how exactly is the background time running if it's not parallel?
这个话题已经讨论了很多,所以我不会再添加一页长的答案。
Tl;dr:是的,Python 多线程对 CPU 密集型任务没有帮助,但对于花费大量时间等待其他事情的任务(网络,Disk-I/O,用户输入,基于时间的事件)。
sleep()
属于后一组任务,所以多线程会加速它,即使它不会同时使用多个内核。
线程启动时 OS 处于控制状态,OS 将在线程之间 context-switch (我相信这是正确的术语) .
time
函数通过 OS 访问计算机上的 时钟 - 该时钟始终为 运行。只要 OS 周期性地给每个线程时间来访问一个 clock 线程的目标就可以判断它是否已经睡了足够长的时间。
线程不是运行并行的,OS周期性地给每个线程一个机会查看时钟。
这里有一些关于正在发生的事情的更详细的信息。我将 Thread 子类化并在调用时将其 run
和 join
方法覆盖为 log。
警告 The documentation specifically states
only override __init__
and run
methods
我很惊讶覆盖 join
没有引起问题。
from time import sleep, perf_counter
from threading import Thread
import pandas as pd
c = {}
def foo(i):
c[i]['foo start'] = perf_counter() - start
sleep(5)
# print(f'{i} - start:{start} end:{perf_counter()}')
c[i]['foo end'] = perf_counter() - start
class Test(Thread):
def __init__(self,*args,**kwargs):
self.i = kwargs['args'][0]
super().__init__(*args,**kwargs)
def run(self):
# print(f'{self.i} - started:{perf_counter()}')
c[self.i]['thread start'] = perf_counter() - start
super().run()
def join(self):
# print(f'{self.i} - joined:{perf_counter()}')
c[self.i]['thread joined'] = perf_counter() - start
super().join()
threads = []
start = perf_counter()
for i in range(10):
c[i] = {}
t = Test(target=foo,args=(i,))
t.start()
threads.append(t)
for i in threads:
i.join()
df = pd.DataFrame(c)
print(df)
0 1 2 3 4 5 6 7 8 9
thread start 0.000729 0.000928 0.001085 0.001245 0.001400 0.001568 0.001730 0.001885 0.002056 0.002215
foo start 0.000732 0.000931 0.001088 0.001248 0.001402 0.001570 0.001732 0.001891 0.002058 0.002217
thread joined 0.002228 5.008274 5.008300 5.008305 5.008323 5.008327 5.008330 5.008333 5.008336 5.008339
foo end 5.008124 5.007982 5.007615 5.007829 5.007672 5.007899 5.007724 5.007758 5.008051 5.007549
希望您能看到所有线程的启动顺序非常接近;一旦 thread 0 加入,在它停止(foo
结束)之前不会发生任何其他事情,然后每个其他线程都加入并终止。
有时一个线程甚至在加入之前就终止了 - 对于线程 one plus foo ends
在线程被加入之前。
我在线程教程中看到如下代码:
from time import sleep, perf_counter
from threading import Thread
start = perf_counter()
def foo():
sleep(5)
threads = []
for i in range(100):
t = Thread(target=foo,)
t.start()
threads.append(t)
for i in threads:
i.join()
end = perf_counter()
print(f'Took {end - start}')
当我 运行 它打印 Took 5.014557975
。好的,那部分没问题。它不像非线程版本那样需要 500 秒。
我不明白 .join
是如何工作的。我注意到没有调用 .join
我得到了 Took 0.007060926999999995
这表明主线程在子线程之前结束。由于 '.join()' 应该阻塞,当循环的第一次迭代发生时,它不会被阻塞并且必须等待 5 秒直到第二次迭代吗?它是如何做到 运行 的?
我一直在阅读 python 线程并不是真正的多线程,它看起来只是(运行s 在单核上),但如果是这样,那么后台时间究竟是多少运行如果不平行就宁?
So '.join()' is supposed to block, so when the first iteration of the loop occurs wont it be blocked and it has to wait 5 seconds till the second iteration?
记住所有的线程都是同时启动的,所有线程都需要 ~5s。
第二个 for
循环等待所有线程完成。第一个线程完成大约需要 5 秒,但其余 99 个线程将大致同时完成,循环的其余 99 次迭代也是如此。
当您在第二个线程上调用 join()
时,它要么已经完成,要么将在几毫秒内完成。
I keep reading python threading is not truly multithreaded and it only appears to be (runs on a single core), but if that is the case then how exactly is the background time running if it's not parallel?
这个话题已经讨论了很多,所以我不会再添加一页长的答案。
Tl;dr:是的,Python 多线程对 CPU 密集型任务没有帮助,但对于花费大量时间等待其他事情的任务(网络,Disk-I/O,用户输入,基于时间的事件)。
sleep()
属于后一组任务,所以多线程会加速它,即使它不会同时使用多个内核。
线程启动时 OS 处于控制状态,OS 将在线程之间 context-switch (我相信这是正确的术语) .
time
函数通过 OS 访问计算机上的 时钟 - 该时钟始终为 运行。只要 OS 周期性地给每个线程时间来访问一个 clock 线程的目标就可以判断它是否已经睡了足够长的时间。
线程不是运行并行的,OS周期性地给每个线程一个机会查看时钟。
这里有一些关于正在发生的事情的更详细的信息。我将 Thread 子类化并在调用时将其 run
和 join
方法覆盖为 log。
警告 The documentation specifically states
only override
__init__
andrun
methods
我很惊讶覆盖 join
没有引起问题。
from time import sleep, perf_counter
from threading import Thread
import pandas as pd
c = {}
def foo(i):
c[i]['foo start'] = perf_counter() - start
sleep(5)
# print(f'{i} - start:{start} end:{perf_counter()}')
c[i]['foo end'] = perf_counter() - start
class Test(Thread):
def __init__(self,*args,**kwargs):
self.i = kwargs['args'][0]
super().__init__(*args,**kwargs)
def run(self):
# print(f'{self.i} - started:{perf_counter()}')
c[self.i]['thread start'] = perf_counter() - start
super().run()
def join(self):
# print(f'{self.i} - joined:{perf_counter()}')
c[self.i]['thread joined'] = perf_counter() - start
super().join()
threads = []
start = perf_counter()
for i in range(10):
c[i] = {}
t = Test(target=foo,args=(i,))
t.start()
threads.append(t)
for i in threads:
i.join()
df = pd.DataFrame(c)
print(df)
0 1 2 3 4 5 6 7 8 9
thread start 0.000729 0.000928 0.001085 0.001245 0.001400 0.001568 0.001730 0.001885 0.002056 0.002215
foo start 0.000732 0.000931 0.001088 0.001248 0.001402 0.001570 0.001732 0.001891 0.002058 0.002217
thread joined 0.002228 5.008274 5.008300 5.008305 5.008323 5.008327 5.008330 5.008333 5.008336 5.008339
foo end 5.008124 5.007982 5.007615 5.007829 5.007672 5.007899 5.007724 5.007758 5.008051 5.007549
希望您能看到所有线程的启动顺序非常接近;一旦 thread 0 加入,在它停止(foo
结束)之前不会发生任何其他事情,然后每个其他线程都加入并终止。
有时一个线程甚至在加入之前就终止了 - 对于线程 one plus foo ends
在线程被加入之前。