线程中 join() 有什么用?

2025-01-20 09:07:00
admin
原创
81
摘要:问题描述:我正在研究 python 线程并遇到了join()。作者说,如果线程处于守护进程模式,那么我需要使用它join(),以便线程可以在主线程终止之前完成。但我也看到他使用t.join()尽管t不是daemon示例代码是这样的import threading import time import logg...

问题描述:

我正在研究 python 线程并遇到了join()

作者说,如果线程处于守护进程模式,那么我需要使用它join(),以便线程可以在主线程终止之前完成。

但我也看到他使用t.join()尽管t不是daemon

示例代码是这样的

import threading
import time
import logging

logging.basicConfig(level=logging.DEBUG,
                    format='(%(threadName)-10s) %(message)s',
                    )

def daemon():
    logging.debug('Starting')
    time.sleep(2)
    logging.debug('Exiting')

d = threading.Thread(name='daemon', target=daemon)
d.setDaemon(True)

def non_daemon():
    logging.debug('Starting')
    logging.debug('Exiting')

t = threading.Thread(name='non-daemon', target=non_daemon)

d.start()
t.start()

d.join()
t.join()

我不知道有什么用,t.join()因为它不是守护进程,即使我删除它我也看不到任何变化


解决方案 1:

一个有点笨拙的 ASCII 艺术来演示该机制:join()大概是由主线程调用的。 它也可以由另一个线程调用,但会不必要地使图表复杂化。

join-调用应该放在主线程的轨道中,但是为了表达线程关系并使其尽可能简单,我选择将其放在子线程中。

    without join:
    +---+---+------------------                     main-thread
        |   |
        |   +...........                            child-thread(short)
        +..................................         child-thread(long)
    
    with join
    +---+---+------------------***********+###      main-thread
        |   |                             |
        |   +...........join()            |         child-thread(short)
        +......................join()......         child-thread(long)

    with join and daemon thread
    +-+--+---+------------------***********+###     parent-thread
      |  |   |                             |
      |  |   +...........join()            |        child-thread(short)
      |  +......................join()......        child-thread(long)
      +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,     child-thread(long + daemonized)

    '-' main-thread/parent-thread/main-program execution
    '.' child-thread execution
    '#' optional parent-thread execution after join()-blocked parent-thread could 
        continue
    '*' main-thread 'sleeping' in join-method, waiting for child-thread to finish
    ',' daemonized thread - 'ignores' lifetime of other threads;
        terminates when main-programs exits; is normally meant for 
        join-independent tasks

因此,您看不到任何变化的原因是因为您的主线程在之后没有执行任何操作join。您可以说join(仅)与主线程的执行流程相关。

例如,如果您想要同时下载一堆页面,然后将它们连接成一个大页面,那么您可以使用线程启动并发下载,但需要等到最后一页/线程完成后才能开始将多个页面组合成一个页面。这时您可以使用join()

解决方案 2:

直接来自文档

join([timeout]) 等待线程终止。这会阻塞调用线程,直到调用其 join() 方法的线程终止(正常终止或通过未处理的异常终止),或者直到发生可选超时。

这意味着产生的主线程td等待t完成,直到它完成。

根据程序所采用的逻辑,您可能需要等到线程完成后主线程才继续。

另请参阅:

可以将线程标记为“守护线程”。此标志的意义在于,当只剩下守护线程时,整个 Python 程序就会退出。

一个简单的例子,假设我们有这个:

def non_daemon():
    time.sleep(5)
    print 'Test non-daemon'

t = threading.Thread(name='non-daemon', target=non_daemon)

t.start()

结尾是:

print 'Test one'
t.join()
print 'Test two'

这将输出:

Test one
Test non-daemon
Test two

这里主线程明确等待t线程完成,直到它print第二次调用。

或者如果我们有这个:

print 'Test one'
print 'Test two'
t.join()

我们将得到这个输出:

Test one
Test two
Test non-daemon

这里我们在主线程中完成工作,然后等待t线程完成。在这种情况下,我们甚至可以删除显式连接t.join(),程序将隐式等待t完成。

解决方案 3:

感谢这个帖子——它也给了我很大的帮助。

我今天学习了一些有关 .join() 的知识。

这些线程并行运行:

d.start()
t.start()
d.join()
t.join()

这些按顺序运行(不是我想要的):

d.start()
d.join()
t.start()
t.join()

具体来说,我试图巧妙而整洁:

class Kiki(threading.Thread):
    def __init__(self, time):
        super(Kiki, self).__init__()
        self.time = time
        self.start()
        self.join()

这有效!但它是按顺序运行的。我可以将 self.start() 放在 init 中,但不能将 self.join() 放在其中。这必须每个线程启动后完成。

join() 会导致主线程等待您的线程完成。否则,您的线程将自行运行。

因此,可以将 join() 视为对主线程的“保留”——它会将您的线程解线程化并在主线程中按顺序执行,然后主线程才能继续。它确保您的线程在主线程向前移动之前完成。请注意,这意味着如果您的线程在调用 join() 之前已经完成,这是可以的——调用 join() 时主线程会立即被释放。

事实上,我现在才意识到主线程在 d.join() 处等待,直到线程 d 完成后才转到 t.join()。

事实上,为了清楚起见,请考虑以下代码:

import threading
import time

class Kiki(threading.Thread):
    def __init__(self, time):
        super(Kiki, self).__init__()
        self.time = time
        self.start()

    def run(self):
        print self.time, " seconds start!"
        for i in range(0,self.time):
            time.sleep(1)
            print "1 sec of ", self.time
        print self.time, " seconds finished!"


t1 = Kiki(3)
t2 = Kiki(2)
t3 = Kiki(1)
t1.join()
print "t1.join() finished"
t2.join()
print "t2.join() finished"
t3.join()
print "t3.join() finished"

它产生这个输出(请注意打印语句如何相互连接。)

$ python test_thread.py
32   seconds start! seconds start!1

 seconds start!
1 sec of  1
 1 sec of 1  seconds finished!
 21 sec of
3
1 sec of  3
1 sec of  2
2  seconds finished!
1 sec of  3
3  seconds finished!
t1.join() finished
t2.join() finished
t3.join() finished
$ 

t1.join() 阻碍了主线程。所有三个线程在 t1.join() 完成之前完成,主线程继续执行打印,然后执行 t2.join(),然后执行打印,然后执行 t3.join(),然后执行打印。

欢迎指正。我对线程也是新手。

(注意:如果您有兴趣,我正在为 DrinkBot 编写代码,我需要线程来同时而不是顺序运行成分泵——等待每杯饮料的时间更少。)

解决方案 4:

方法 join()

阻塞调用线程,直到调用其 join() 方法的线程终止。

来源: http: //docs.python.org/2/library/threading.html

解决方案 5:

使用 join - 解释器将等待,直到您的进程完成终止

>>> from threading import Thread
>>> import time
>>> def sam():
...   print 'started'
...   time.sleep(10)
...   print 'waiting for 10sec'
... 
>>> t = Thread(target=sam)
>>> t.start()
started

>>> t.join() # with join interpreter will wait until your process get completed or terminated
done?   # this line printed after thread execution stopped i.e after 10sec
waiting for 10sec
>>> done?

如果没有 join,解释器就不会等到进程终止

>>> t = Thread(target=sam)
>>> t.start()
started
>>> print 'yes done' #without join interpreter wont wait until process get terminated
yes done
>>> waiting for 10sec

解决方案 6:

此示例演示了该.join()操作:

import threading
import time

def threaded_worker():
    for r in range(10):
        print('Other: ', r)
        time.sleep(2)

thread_ = threading.Timer(1, threaded_worker)
thread_.daemon = True  # If the main thread is killed, this thread will be killed as well. 
thread_.start()

flag = True

for i in range(10):
    print('Main: ', i)
    time.sleep(2)
    if flag and i > 4:
        print(
            '''
            Threaded_worker() joined to the main thread. 
            Now we have a sequential behavior instead of concurrency.
            ''')
        thread_.join()
        flag = False

出去:

Main:  0
Other:  0
Main:  1
Other:  1
Main:  2
Other:  2
Main:  3
Other:  3
Main:  4
Other:  4
Main:  5
Other:  5

            Threaded_worker() joined to the main thread. 
            Now we have a sequential behavior instead of concurrency.
            
Other:  6
Other:  7
Other:  8
Other:  9
Main:  6
Main:  7
Main:  8
Main:  9

解决方案 7:

在 python 3.x 中,join() 用于将一个线程与主线程连接起来,即,当 join() 用于特定线程时,主线程将停止执行,直到连接的线程执行完成。

#1 - Without Join():
import threading
import time
def loiter():
    print('You are loitering!')
    time.sleep(5)
    print('You are not loitering anymore!')

t1 = threading.Thread(target = loiter)
t1.start()
print('Hey, I do not want to loiter!')
'''
Output without join()--> 
You are loitering!
Hey, I do not want to loiter!
You are not loitering anymore! #After 5 seconds --> This statement will be printed

'''
#2 - With Join():
import threading
import time
def loiter():
    print('You are loitering!')
    time.sleep(5)
    print('You are not loitering anymore!')

t1 = threading.Thread(target = loiter)
t1.start()
t1.join()
print('Hey, I do not want to loiter!')

'''
Output with join() -->
You are loitering!
You are not loitering anymore! #After 5 seconds --> This statement will be printed
Hey, I do not want to loiter! 

'''

解决方案 8:

join(t)为非守护线程和守护线程编写函数时,主线程(或主进程)应该等待t几秒钟,然后才能继续处理自己的进程。在这t几秒钟的等待时间内,两个子线程都应该做它们能做的事情,比如打印出一些文本。t几秒钟后,如果非守护线程仍未完成其工作,它仍然可以在主进程完成其工作后完成它的工作,但对于守护线程来说,它只是错过了机会窗口。然而,它最终会在 python 程序退出后死亡。如果有错误,请纠正我。

解决方案 9:

主线程(或任何其他线程)加入其他线程的原因有以下几种

  1. 线程可能已创建或持有(锁定)某些资源。调用 join 的线程可能能够自行清除这些资源

  2. join() 是一个自然的阻塞调用,以便调用 join 的线程在被调用的线程终止后继续运行。

如果python程序没有加入其他线程,python解释器仍然会为其加入非守护线程。

解决方案 10:

  • join()等待非守护进程和守护进程线程完成。

  • 如果没有join(),非守护线程正在运行并与主线程同时完成。

  • 如果没有join(),守护线程会与主线程同时运行,当主线程完成时,如果守护线程仍在运行,则守护线程将退出而不完成。

因此,使用下面的join()daemon=False(守护进程线程)(默认情况下为守护False进程):

import time
from threading import Thread

def test1():
    for _ in range(3):
        print("Test1 is running...")
        time.sleep(1)
    print("Test1 is completed")
    
def test2():
    for _ in range(3):
        print("Test2 is running...")
        time.sleep(1)
    print("Test2 is completed")
                               # Here
thread1 = Thread(target=test1, daemon=False)
thread2 = Thread(target=test2, daemon=False)
                               # Here
thread1.start()
thread2.start()
thread1.join() # Here
thread2.join() # Here
print("Main is completed")

或者,使用下面的join()daemon=True(非守护线程)

# ...
                               # Here
thread1 = Thread(target=test1, daemon=True)
thread2 = Thread(target=test2, daemon=True)
                               # Here
# ...
thread1.join() # Here
thread2.join() # Here
print("Main is completed")

join()等待Test1Test2非守护进程或守护进程线程完成。因此,在线程Main is completed完成后会打印,如下所示:Test1`Test2`

Test1 is running...
Test2 is running...
Test1 is running...
Test2 is running...
Test1 is running...
Test2 is running...
Test1 is completed
Test2 is completed
Main is completed

并且,如果不使用join()并且如果daemon=False(非守护线程)如下:

# ...
                               # Here
thread1 = Thread(target=test1, daemon=False)
thread2 = Thread(target=test2, daemon=False)
                               # Here
# ...
# thread1.join()
# thread2.join()
print("Main is completed")

Test1Test2非守护线程与主线程同时运行并完成。因此,在线程Main is completed完成之前打印Test1Test2如下所示:

Test1 is running...
Test2 is running...
Main is completed
Test1 is running...
Test2 is running...
Test1 is running...
Test2 is running...
Test1 is completed
Test2 is completed

并且,如果不使用join()并且如果daemon=True(守护线程)如下:

# ...
                               # Here
thread1 = Thread(target=test1, daemon=True)
thread2 = Thread(target=test2, daemon=True)
                               # Here
# ...
# thread1.join()
# thread2.join()
print("Main is completed")

Test1Test2守护线程与主线程同时运行。因此,在守护线程完成之前以及主线程完成时Main is completed打印,并且守护线程未完成就退出,如下所示:Test1`Test2****Test1`Test2

Test1 is running...
Test2 is running...
Main is completed

解决方案 11:

看起来这里误解了同步和异步处理之间的区别。

线程用于执行子程序,大多数情况下采用“并行”或“并发”方式(取决于设备是否具有多处理器)。但是,并发的意义何在?大多数情况下,它是通过应用“分而治之”的思想来提高进程的性能。让多个线程(子进程)同时执行整个进程的“一部分” ,然后有一个“最后”步骤,将所有子进程的结果合并(连接;因此称为“连接”方法)。

当然,为了实现这种效率提升,划分为线程的部分必须是“互斥的”(即,它们不共享要更新的值……——在并行计算中称为“关键部分”——)。如果至少有一个值被两个或多个线程更新,那么一个线程必须等待另一个线程“完成”其更新,否则将获得不一致的结果(即,两个拥有银行账户的人打算在 ATM 机上提取一定数量的钱……如果没有适当的机制来“锁定”或“保护”两个 ATM 机中的变量“余额”,则提款将完全搞砸余额的最终值,给账户所有者造成明显的严重财务问题)。

因此,回到并行计算中线程的目的:让所有线程执行各自的部分,并使用“连接”使它们“返回”到主进程,以便每个单独的结果都被“合并”为一个全局结果。

例子?有很多,但我们只列举几个解释清楚的例子:

  • 矩阵乘法:让每个线程将矩阵 A 的一个向量与整个第二个矩阵 B 相乘,以获得矩阵 C 的一个向量。最后,将所有结果放在一起以“显示”(显示)结果:矩阵 C。在此示例中,尽管矩阵 B 被所有线程使用,但它的任何值都不会更新或修改(只读)。

  • 求和,对大量数字的数组(包含数千个值的数组,无论是整数还是浮点数)进行乘积。创建线程来执行部分求和/乘积(例如,如果您必须对 10K 个值求和,则创建 5 个线程,每个线程包含 2K 个值);然后使用“join”使它们返回主进程并对所有 5 个线程的单独结果求和。

理论上,该进程将执行 2000 + 5 个步骤(5 个线程同时执行 2000 个步骤,加上主进程中最后 5 个小计的和)。但在实践中,5 个线程完成 2000 个数字的和需要多长时间完全是可变的,因为这里涉及不同的因素(处理器速度、电流、是否是 Web 服务、网络延迟等)。但是,在“最坏情况下”,投入的时间量将是“最慢”线程所花费的时间,加上 5 个结果步骤的最终和。而且,在实践中,一个用于完成整个工作的 20% 的线程,不太可能比完成 100% 工作的单个顺序过程花费更长的时间(当然,这还取决于要处理的样本的大小...对于 10K 个值的总和,与使用相同的 5 个线程对 10 个值的总和相比,优势不会相同...这是不切实际的,不值得的)。

  • 快速排序:我们都知道快速排序的工作原理。但是,如果我们在两个线程中执行它,就有机会对其进行改进:一个线程处理奇数,另一个线程处理偶数。然后递归执行,在某个时刻它会合并两个线程的结果,并以一种不需要多次重复的方式进行最终的快速排序,因为两个线程完成其初始工作后,数字将足够有序。对于数量相当大且无序的项目,这可以大大提高性能。有可能通过对其背后的逻辑进行某种安排来使用三个线程,但其增益确实很小,不值得进行编程。但是,两个线程具有不错的性能(时间)增益。

因此,在 Python 中使用“join”(或其他“并发”语言中的等效词)具有重要意义;但很大程度上取决于程序员对他/她想要“并行化”什么的理解,以及他/她在将算法拆分为正确的步骤以进行并行化方面的技能以及哪些步骤需要保留在主流程中。这更多的是一个“逻辑”思维问题,而不是编程“反模式”。

解决方案 12:

你可能会问:“使用 join() 有什么用?”实际上,这与“关闭文件有什么用,因为 Python 和操作系统会在程序退出时帮我关闭文件?”的答案相同。

这只是一个好的编程问题。您应该在代码中线程不再运行的位置 join() 您的线程,因为您必须确保线程不会在运行以干扰您自己的代码,或者您希望在更大的系统中正常运行。

您可能会说“我不希望我的代码延迟给出答案”,只是因为 join() 可能需要额外的时间。在某些情况下,这可能完全有效,但现在您需要考虑到您的代码“留下了一堆垃圾,需要 Python 和操作系统来清理”。如果您出于性能原因这样做,我强烈建议您记录该行为。如果您正在构建一个其他人可能使用的库/包,则尤其如此。

除了性能原因之外,没有理由不使用 join(),而且我认为你的代码不需要表现得那么好。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用