欢迎光临
我们一直在努力

python基础线程-管理并发线程

线程模块建立在线程的底层特性之上,使线程的工作变得更简单、更像python。使用线程允许程序在同一进程空间中并发运行多个操作。

私信小编01即可获取Python学习资料

线程对象

使用线程最简单的方法是用目标函数实例化它,然后调用start()让它开始工作。

import?threading

def?worker():
????"""线程worker函数"""
????print('Worker')
????return

threads?=?[]
for?i?in?range(5):
????t?=?threading.Thread(target=worker)
????threads.append(t)
????t.start()

结果:输出为五行,每行上都有“Worker”

nbsp;python3?threading_simple.py

Worker
Worker
Worker
Worker
Worker

能够生成一个线程并传递参数来告诉它要做什么工作是很有用的。这个例子传递一个数字,然后线程打印这个数字。

import?threading

def?worker(num):
????"""线程worker函数"""
????print('Worker:?{num}'?)
????return

threads?=?[]
for?i?in?range(5):
????t?=?threading.Thread(target=worker,?args=(i,))
????threads.append(t)
????t.start()

现在每个线程打印的消息包含一个数字:

nbsp;python3?-u?threading_simpleargs.py

Worker:?0
Worker:?1
Worker:?2
Worker:?3
Worker:?4

确定当前线程

使用参数来标识或命名线程很麻烦,而且没有必要。每个Thread实例都有一个具有默认值的名称,该名字可以在创建线程时更改。命名线程在具有多个服务线程来处理不同操作的服务器进程中很有用。

import?threading
import?time

def?worker():
????print(threading.currentThread().getName(),?'开始运行')
????time.sleep(2)
????print(threading.currentThread().getName(),?'结束运行')

def?my_service():
????print(threading.currentThread().getName(),?'开始运行')
????time.sleep(3)
????print(threading.currentThread().getName(),?'结束运行')

t?=?threading.Thread(name='my_service',?target=my_service)
w?=?threading.Thread(name='worker',?target=worker)
w2?=?threading.Thread(target=worker)?#?使用默认名字

w.start()
w2.start()
t.start()

调试输出包括每行上当前线程的名称。“线程名称”列中带有“Thread-1”的行对应于未命名的线程w2。

nbsp;python?-u?threading_names.py

worker?Thread-1?开始运行
my_service?开始运行
开始运行
Thread-1worker?结束运行
结束运行
my_service?结束运行

大多数程序不使用打印进行调试。logging支持使用格式化程序代码%(threadName)在每个日志消息中嵌入线程名称。在日志消息中包含线程名称可以更容易地将这些消息追溯到其源。

import?logging
import?threading
import?time

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


def?worker():
????logging.debug('开始运行')
????time.sleep(2)
????logging.debug('结束运行')


def?my_service():
????logging.debug('开始运行')
????time.sleep(3)
????logging.debug('结束运行')


t?=?threading.Thread(name='my_service',?target=my_service)
w?=?threading.Thread(name='worker',?target=worker)
w2?=?threading.Thread(target=worker)??#?使用默认名字

w.start()
w2.start()
t.start()

logging是线程安全的,因此来自不同线程的消息在输出中保持不同。

nbsp;python?threading_names_log.py

[DEBUG]?(worker????)?开始运行
[DEBUG]?(Thread-1??)?开始运行
[DEBUG]?(my_service)?开始运行
[DEBUG]?(worker????)?结束运行
[DEBUG]?(Thread-1??)?结束运行
[DEBUG]?(my_service)?结束运行

守护程序与非守护程序线程

到目前为止,示例程序已隐式地等待退出,直到所有线程都完成了它们的工作。有时程序生成一个线程作为守护进程运行,而该线程在运行时不会阻止主程序退出。使用守护进程线程对于那些可能无法轻松中断线程或让线程在其工作过程中死亡不会丢失或损坏数据的服务(例如,为服务监视工具生成“心跳”的线程)非常有用。要将线程标记为守护程序,请使用布尔参数调用其setDaemon()方法。默认情况下,线程不是守护程序,因此传递True将打开守护程序模式。

import?threading
import?time
import?logging

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


def?daemon():
????logging.debug('开始运行')
????time.sleep(2)
????logging.debug('结束运行')


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


def?non_daemon():
????logging.debug('开始运行')
????logging.debug('结束运行')


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

d.start()
t.start()

请注意,输出不包括来自守护进程线程的“结束运行”消息,因为所有非守护进程线程(包括主线程)都在守护进程线程从其两秒钟的睡眠中唤醒之前退出。

nbsp;python?threading_daemon.py

(daemon????)?开始运行
(non-daemon)?开始运行
(non-daemon)?结束运行

要等到守护进程线程完成其工作,请使用join()方法。

import?threading
import?time
import?logging

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

def?daemon():
????logging.debug('开始运行')
????time.sleep(2)
????logging.debug('结束运行')

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

def?non_daemon():
????logging.debug('开始运行')
????logging.debug('结束运行')

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

d.start()
t.start()

d.join()
t.join()

等待守护线程使用join()退出意味着它有机会生成“结束运行”消息。

nbsp;python?threading_daemon_join.py

(daemon????)?开始运行
(non-daemon)?开始运行
(non-daemon)?结束运行
(daemon????)?结束运行

默认情况下,join()无限期阻塞。也可以传递一个超时参数(一个浮点数,表示等待线程变为非活动状态的秒数)。如果线程没有在超时时间内完成,join()仍然返回。

import?threading
import?time
import?logging

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

def?daemon():
????logging.debug('开始运行')
????time.sleep(2)
????logging.debug('结束运行')

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

def?non_daemon():
????logging.debug('开始运行')
????logging.debug('结束运行')

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

d.start()
t.start()

d.join(1)
#?python2写法
print?('d.isAlive()',?d.isAlive())
#?python3写法
print?('d.is_alive()',?d.is_alive())
t.join()

由于传递的超时值小于守护进程线程的睡眠时间,因此join()返回后,线程仍然是“活动的”。

nbsp;python?threading_daemon_join_timeout.py

(daemon????)?开始运行
(non-daemon)?开始运行
(non-daemon)?结束运行
d.isAlive()?True
d.is_alive()?True

枚举所有线程

没有必要保留所有守护进程线程的显式句柄,以确保它们在退出主进程之前已完成。enumerate()返回活动线程实例的列表。该列表包含当前线程,并且由于不允许加入当前线程(这会导致死锁情况),因此必须跳过它。

import?random
import?threading
import?time
import?logging

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

def?worker():
????"""线程worker函数"""
????t?=?threading.currentThread()
????pause?=?random.randint(1,5)
????logging.debug('sleeping?%s',?pause)
????time.sleep(pause)
????logging.debug('ending')
????return

for?i?in?range(3):
????t?=?threading.Thread(target=worker)
????t.setDaemon(True)
????t.start()

main_thread?=?threading.currentThread()
for?t?in?threading.enumerate():
????if?t?is?main_thread:
????????continue
????logging.debug(f'joining?{?t.getName()}')
????t.join()

由于worker睡眠的时间是随机的,所以这个程序的输出可能会有所不同。应该是这样的:

nbsp;python?threading_enumerate.py

(Thread-1??)?sleeping?3
(Thread-2??)?sleeping?2
(Thread-3??)?sleeping?5
(MainThread)?joining?Thread-1
(Thread-2??)?ending
(Thread-1??)?ending
(MainThread)?joining?Thread-3
(Thread-3??)?ending
(MainThread)?joining?Thread-

子类化线程

在启动时,线程进行一些基本的初始化,然后调用其run()方法,该方法调用传递给构造函数的目标函数。若要创建Thread的子类,请重写run()以执行任何必要的操作。

import?threading
import?logging

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

class?MyThread(threading.Thread):

????def?run(self):
????????logging.debug('running')
????????return

for?i?in?range(5):
????t?=?MyThread()
????t.start()

忽略run()的返回值。

nbsp;python?threading_subclass.py

(Thread-1??)?running
(Thread-2??)?running
(Thread-3??)?running
(Thread-4??)?running
(Thread-5??)?running

因为传递给Thread构造函数的args和kwargs值保存在私有变量中,因此不容易从子类访问它们。要将参数传递给自定义线程类型,请重新定义构造函数以将值保存在实例属性中,该属性可以在子类中看到。

import?threading
import?logging

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

class?MyThreadWithArgs(threading.Thread):

????def?__init__(self,?group=None,?target=None,?name=None,
?????????????????args=(),?kwargs=None,?verbose=None):
????????threading.Thread.__init__(self,?group=group,?target=target,?name=name,
??????????????????????????????????verbose=verbose)
????????self.args?=?args
????????self.kwargs?=?kwargs
????????return

????def?run(self):
????????logging.debug('running?with?%s?and?%s',?self.args,?self.kwargs)
????????return

for?i?in?range(5):
????t?=?MyThreadWithArgs(args=(i,),?kwargs={'a':'A',?'b':'B'})
????t.start()

MyThreadWithArgs与Thread使用相同的API,但是与其他任何类一样,另一个类可以轻松地更改构造函数方法以采用与线程目的更直接相关的更多或不同的参数。

nbsp;python?threading_subclass_args.py

(Thread-1??)?running?with?(0,)?and?{'a':?'A',?'b':?'B'}
(Thread-2??)?running?with?(1,)?and?{'a':?'A',?'b':?'B'}
(Thread-3??)?running?with?(2,)?and?{'a':?'A',?'b':?'B'}
(Thread-4??)?running?with?(3,)?and?{'a':?'A',?'b':?'B'}
(Thread-5??)?running?with?(4,)?and?{'a':?'A',?'b':?'B'}

计时器线程

Timer提供了一个将Thread子类化的原因的示例,它也包含在线程中。计时器在延迟之后开始工作,并且可以在该延迟时间段内的任何时间取消。

import?threading
import?time
import?logging

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

def?delayed():
????logging.debug('worker?running')
????return

t1?=?threading.Timer(3,?delayed)
t1.setName('t1')
t2?=?threading.Timer(3,?delayed)
t2.setName('t2')

logging.debug('starting?timers')
t1.start()
t2.start()

logging.debug('waiting?before?canceling?%s',?t2.getName())
time.sleep(2)
logging.debug('canceling?%s',?t2.getName())
t2.cancel()
logging.debug('done')

请注意,第二个计时器从不运行,而第一个计时器似乎在主程序的其余部分完成后运行。因为它不是守护进程线程,所以当主线程完成时,它是隐式连接的。

nbsp;python?threading_timer.py

(MainThread)?starting?timers
(MainThread)?waiting?before?canceling?t2
(MainThread)?canceling?t2
(MainThread)?done
(t1????????)?worker?running

线程间信令

虽然使用多个线程的目的是分离出单独的操作以并发运行,但有时能够在两个或多个线程中同步这些操作是很重要的。线程之间通信的一个简单方法是使用事件对象。事件管理内部标志,调用者可以set()或clear()。其他线程可以wait()设置set(),有效地阻止进程,直到允许继续。

import?logging
import?threading
import?time

logging.basicConfig(level=logging.DEBUG,
????????????????????format='(%(threadName)-10s)?%(message)s',
????????????????????)
????????????????????
def?wait_for_event(e):
????"""Wait?for?the?event?to?be?set?before?doing?anything"""
????logging.debug('wait_for_event?starting')
????event_is_set?=?e.wait()
????logging.debug('event?set:?%s',?event_is_set)

def?wait_for_event_timeout(e,?t):
????"""Wait?t?seconds?and?then?timeout"""
????while?not?e.isSet():
????????logging.debug('wait_for_event_timeout?starting')
????????event_is_set?=?e.wait(t)
????????logging.debug('event?set:?%s',?event_is_set)
????????if?event_is_set:
????????????logging.debug('processing?event')
????????else:
????????????logging.debug('doing?other?work')


e?=?threading.Event()
t1?=?threading.Thread(name='block',?
??????????????????????target=wait_for_event,
??????????????????????args=(e,))
t1.start()

t2?=?threading.Thread(name='non-block',?
??????????????????????target=wait_for_event_timeout,?
??????????????????????args=(e,?2))
t2.start()

logging.debug('Waiting?before?calling?Event.set()')
time.sleep(3)
e.set()
logging.debug('Event?is?set')

wait()方法在等待表示时间的参数之前占用了秒数。它返回一个布尔值,指示是否设置了事件,因此调用者知道wait()返回的原因。isSet()方法可以单独用于事件,而不必担心阻塞。

在本例中,wait_for_event_timeout()检查事件状态,不会无限期阻塞。wait_for_event()会阻塞对wait()的调用,直到事件状态更改后才会返回。

nbsp;python?threading_event.py

(block?????)?wait_for_event?starting
(non-block?)?wait_for_event_timeout?starting
(MainThread)?Waiting?before?calling?Event.set()
(non-block?)?event?set:?False
(non-block?)?doing?other?work
(non-block?)?wait_for_event_timeout?starting
(MainThread)?Event?is?set
(block?????)?event?set:?True
(non-block?)?event?set:?True
(non-block?)?processing?event
 收藏 (0) 打赏

您可以选择一种方式赞助本站

支付宝扫一扫赞助

微信钱包扫描赞助

未经允许不得转载:英协网 » python基础线程-管理并发线程

分享到: 生成海报
avatar

热门文章

  • 评论 抢沙发

    • QQ号
    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址

    登录

    忘记密码 ?

    切换登录

    注册

    我们将发送一封验证邮件至你的邮箱, 请正确填写以完成账号注册和激活