返回介绍

建议48:使用 threading 模块编写多线程程序

发布于 2024-01-30 22:19:09 字数 3351 浏览 0 评论 0 收藏 0

GIL的存在使得Python多线程编程暂时无法充分利用多处理器的优势,这种限制也许使很多人感到沮丧,但事实上这并不意味着我们需要放弃多线程。的确,对于只含纯Python的代码也许使用多线程并不能提高运行速率,但在以下几种情况,如等待外部资源返回,或者为了提高用户体验而建立反应灵活的用户界面,或者多用户应用程序中,多线程仍然是一个比较好的解决方案。Python为多线程编程提供了两个非常简单明了的模块:thread和threading。那么,这两个模块在多线程处理上有什么区别呢?简单一点说:thread模块提供了多线程底层支持模块,以低级原始的方式来处理和控制线程,使用起来较为复杂;而threading模块基于thread进行包装,将线程的操作对象化,在语言层面提供了丰富的特性。Python多线程支持用两种方式来创建线程:一种是通过继承Thread类,重写它的run()方法(注意,不是start()方法);另一种是创建一个threading.Thread对象,在它的初始化函数(__init__())中将可调用对象作为参数传入。实际应用中,推荐优先使用threading模块而不是thread模块,(除非有特殊需要)。下面来具体分析一下这么做的原因。

1)threading模块对同步原语的支持更为完善和丰富。就线程的同步和互斥来说,thread模块只提供了一种锁类型thread.LockType,而threading模块中不仅有Lock指令锁、RLock可重入指令锁,还支持条件变量Condition、信号量Semaphore、BoundedSemaphore以及Event事件等。

2)threading模块在主线程和子线程交互上更为友好,threading中的join()方法能够阻塞当前上下文环境的线程,直到调用此方法的线程终止或到达指定的timeout(可选参数)。利用该方法可以方便地控制主线程和子线程以及子线程之间的执行。来看一个简单示例:

import threading, time,sys
class test(threading.Thread):         
  def __init__(self,name,delay):
    threading.Thread.__init__(self)
    self.name = name
    self.delay = delay
  def run(self):
    print "%s delay for %s" %(self.name,self.delay)
    time.sleep(self.delay)
    c = 0
    while True:      
      print "This is thread %s on line %s" %(self.name,c)
      c = c + 1 
      if c == 3:
        print "End of thread %s" % self.name
        break
t1 = test('Thread 1', 2)
t2 = test('Thread 2', 2)
t1.start()
print "Wait t1 to end"
t1.join()
t2.start()
print 'End of main'

上面的例子中,主线程main在t1上使用join()的方法,主线程会等待t1结束后才继续运行后面的语句,由于线程t2的启动在join语句之后,t2一直等到t1退出后才会开始运行。输出结果如图4-6所示。

图4-6 多线程示例输出结果

3)thread模块不支持守护线程。thread模块中主线程退出的时候,所有的子线程不论是否还在工作,都会被强制结束,并且没有任何警告也没有任何退出前的清理工作。来看一个例子:

from thread import start_new_thread
import time
def myfunc(a,delay):
    print "I will calculate square of  %s after delay for %s" %(a,delay)
    time.sleep(delay)
    print "calculate begins..."
    result = a*a
    print result
    return result
start_new_thread(myfunc,(2,5))#
同时启动两个线程
start_new_thread(myfunc,(6,8))
time.sleep(1)

运行程序,输出结果如下,你会发现子线程的结果还未返回就已经结束了。

I will calculate square of  2 after delay for 5I will calculate square of  6 after delya for 2

这是一种非常野蛮的主线程和子线程的交互方式。如果把主线程和子线程组成的线程组比作一个团队的话,那么主线程应该是这个团队的管理者,它了解每个线程所做的事情、所需的数据输入以及子线程结束时的输出,并把各个线程的输出组合形成有意义的结果。如果一个团队中管理者采取这种强硬的管理方式,相信很多下属都会苦不堪言,因为不仅没有被尊重的感觉,而且还有可能因为这种强势带来决策上的失误。实际上很多情况下我们可能希望主线程能够等待所有子线程都完成时才退出,这时应该使用threading模块,它支持守护线程,可以通过setDaemon()函数来设定线程的daemon属性。当daemon属性设置为True的时候表明主线程的退出可以不用等待子线程完成。默认情况下,daemon标志为False,所有的非守护线程结束后主线程才会结束。来看具体的例子,当daemon属性设置为False,默认主线程会等待所有子线程结束才会退出。将t2的daemon属性改为True之后即使t2运行未结束主线程也会直接退出。

import threading
import time
def myfunc(a,delay):
     print "I will calculate square of  %s after delay for %s" %(a,delay)
     time.sleep(delay)
     print "calculate begins..."
     result = a*a
     print result
     return result
t1=threading.Thread(target=myfunc,args=(2,5))
t2=threading.Thread(target=myfunc,args=(6,8))
print t1.isDaemon()
print t2.isDaemon()
t2.setDaemon(True)                    #
设置守护线程
t1.start()
t2.start()

4)Python3中已经不存在thread模块。thread模块在Python3中被命名为_thread,这种更改主要是为了更进一步明确表示与thread模块相关的更多的是具体的实现细节,它更多展示的是操作系统层面的原始操作和处理。在一般的代码中不应该选择thread模块。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文