python并发编程----对《cookbook》第十二章补充--12.1 启动与停止线程

问题

你要为需要并发执行的代码创建/销毁线程

解决方案

threading 库可以在单独的线程中执行任何的在 Python 中可以调用的对象。你可以创建一个 Thread 对象并将你要执行的对象以 target 参数的形式提供给该对象。 下面是一个简单的例子:

# Code to execute in an independent thread
import time
def countdown(n):
    while n > 0:
        print('T-minus', n)
        n -= 1
        time.sleep(5)

# Create and launch a thread
from threading import Thread
t = Thread(target=countdown, args=(10,))
t.start()

当你创建好一个线程对象后,该对象并不会立即执行,除非你调用它的 start() 方法(当你调用 start() 方法时,它会调用你传递进来的函数,并把你传递进来的参数传递给该函数)。Python中的线程会在一个单独的系统级线程中执行(比如说一个 POSIX 线程或者一个 Windows 线程),这些线程将由操作系统来全权管理。线程一旦启动,将独立执行直到目标函数返回。你可以查询一个线程对象的状态,看它是否还在执行:

if t.is_alive():
    print('Still running')
else:
    print('Completed')

你也可以将一个线程加入到当前线程,并等待它终止:

t.join()

补充:一个join()实例,线程t在一直运行,线程t2在等线程t1结束后在运行:

from threading import Thread
import time

def count1():
    print("count1 start")
    for i in range(5):
        print("count1:%s" % i)
        time.sleep(2)
    print("count1 stop")

def count2():
    print("count2 start")
    for i in range(5):
        print("count2:%s" % i)
        time.sleep(2)
    print("count2 stop")

def count():
    print("count start")
    for i in range(10):
        print("count:%s" % i)
        time.sleep(2)
    print("count stop")

t=Thread(target=count)
t1=Thread(target=count1)
t2=Thread(target=count2)
t.start()
t1.start()

t1.join()
t2.start()
t2.join()
t.join()

输出:

count start
count:0
count1 start
count1:0
count1:1
count:1
count1:2
count:2
count:3
count1:3
count1:4
count:4
count:5
count1 stop
count2 start
count2:0
count2:1
count:6
count2:2
count:7
count2:3
count:8
count:9
count2:4
count stop
count2 stop

Python解释器直到所有线程都终止前仍保持运行。对于需要长时间运行的线程或者需要一直运行的后台任务,你应当考虑使用后台线程。 例如:

t = Thread(target=countdown, args=(10,), daemon=True)
t.start()

补充:

在脚本运行过程中有一个主线程,若在主线程中创建了子线程,当主线程结束时根据子线程daemon属性值的不同可能会发生下面的两种情况之一:

如果某个子线程的daemon属性为False,主线程结束时会检测该子线程是否结束,如果该子线程还在运行,则主线程会等待它完成后再退出;

如果某个子线程的daemon属性为True,主线程运行结束时不对这个子线程进行检查而直接退出,同时所有daemon值为True的子线程将随主线程一起结束,而不论是否运行完成。

属性daemon的值默认为False,如果需要修改,必须在调用start()方法启动线程之前进行设置。另外要注意的是,上面的描述并不适用于IDLE环境中的交互模式或脚本运行模式,因为在该环境中的主线程只有在退出Python IDLE时才终止。

如果线程执行一些像I/O这样的阻塞操作,那么通过轮询来终止线程将使得线程之间的协调变得非常棘手。比如,如果一个线程一直阻塞在一个I/O操作上,它就永远无法返回,也就无法检查自己是否已经被结束了。要正确处理这些问题,你需要利用超时循环来小心操作线程。 例子如下:

class IOTask:
    def terminate(self):
        self._running = False

    def run(self, sock):
        # sock is a socket
        sock.settimeout(5)        # Set timeout period
        while self._running:
            # Perform a blocking I/O operation w/ timeout
            try:
                data = sock.recv(8192)
                break
            except socket.timeout:
                continue
            # Continued processing
            ...
        # Terminated
        return

讨论

由于全局解释锁(GIL)的原因,Python 的线程被限制到同一时刻只允许一个线程执行这样一个执行模型。所以,Python 的线程更适用于处理I/O和其他需要并发执行的阻塞操作(比如等待I/O、等待从数据库获取数据等等),而不是需要多处理器并行的计算密集型任务。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值