线程模块建立在线程的底层特性之上,使线程的工作变得更简单、更像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”

$ 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()

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

$ 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。

$ 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是线程安全的,因此来自不同线程的消息在输出中保持不同。

$ 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()

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

$ 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()退出意味着它有机会生成“结束运行”消息。

$ 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()返回后,线程仍然是“活动的”。

$ 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睡眠的时间是随机的,所以这个程序的输出可能会有所不同。应该是这样的:

$ 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()的返回值。

$ 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,但是与其他任何类一样,另一个类可以轻松地更改构造函数方法以采用与线程目的更直接相关的更多或不同的参数。

$ 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')

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

$ 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()的调用,直到事件状态更改后才会返回。

$ 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

python线程wait_python基础线程-管理并发线程相关推荐

  1. mysql线程挣用问题_MySQL 并发线程的理解

    这几天碰到5.7低版本,并发量提升就会触发bug,导致服务器重新启动现象. 在资源有限的情况下,怎样去有效的进行控制并发,MySQL到底能支持多少并发. 并发实现方式 先分析一下官方对于这方面的建议: ...

  2. java定时器阻塞主线程_Java基础_死锁、线程组、定时器Timer

    一.死锁问题: 死锁是这样一种情形:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放.由于线程被无限期地阻塞,因此程序不可能正常终止. 比如,线程一需要第一把所,此时锁处于空闲状态,给了 ...

  3. 线程学习基础(1):单线程爬虫和多线程爬虫的效率比照

    线程学习基础:单线程爬虫和多线程爬虫的效率比照 1. 并发线程的需求 2. 线程提速方法 3. 如何选择并发编程的三种方式 3.1 什么是CPU密集型计算.IO密集型计算? 3.1.1 CPU密集型( ...

  4. Java 编程问题:十、并发-线程池、可调用对象和同步器

    原文:Java Coding Problems 协议:CC BY-NC-SA 4.0 贡献者:飞龙 本文来自[ApacheCN Java 译文集],自豪地采用谷歌翻译. 本章包括涉及 Java 并发的 ...

  5. [.Net线程处理系列]专题二:线程池中的工作者线程

    目录: 一.上节补充 二.CLR线程池基础 三.通过线程池的工作者线程实现异步 四.使用委托实现异步 五.任务 六.小结 一.上节补充 对于Thread类还有几个常用方法需要说明的. 1.1 Susp ...

  6. java 线程池的使用_Java 使用线程池执行若干任务

    在执行一系列带有IO操作(例如下载文件),且互不相关的异步任务时,采用多线程可以很极大的提高运行效率.线程池包含了一系列的线程,并且可以管理这些线程.例如:创建线程,销毁线程等.本文将介绍如何使用Ja ...

  7. python网络编程基础(线程与进程、并行与并发、同步与异步、阻塞与非阻塞、CPU密集型与IO密集型)...

    python网络编程基础(线程与进程.并行与并发.同步与异步.阻塞与非阻塞.CPU密集型与IO密集型) 目录 线程与进程并行与并发同步与异步阻塞与非阻塞CPU密集型与IO密集型 线程与进程 进程 前言 ...

  8. python 基础 进程,线程,协程,并发并行,异步同步的定义

    进程,线程,协程 进程:是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础. ​ 用大白话说就是系统执行某一项过程的行为,比如你用浏览器在线播放器 ...

  9. Python之路 33:进程与线程概念及基础使用

    关于多进程与多线程基础的学习,必要的概念很多,但我觉得不着急去一次性去死记 多进程与多线程的基础使用很简单,几个方法只需上手 运行一下 文中的代码就可以很快熟悉起来 进阶部分篇幅有点长,放在了另一篇文 ...

最新文章

  1. 《剑指offer》跳台阶
  2. 利用Packer自定义镜像创建容器集群
  3. ch4 MySQL 安全管理
  4. 大型网站技术架构(五)--网站高可用架构(转)
  5. 广数工业机器人五点法_广州数控工业机器人GR-C控制系统操作说明书(2014年11月.pdf...
  6. JS 阻止浮层弹窗下滚动
  7. JQ对复选框全选、获取复选框的值、回选
  8. VmwareTools工具安装
  9. 旋转机械设备故障诊断的轴心轨迹总结
  10. nginx代理图片和视频
  11. 深入浅出零知识证明(一):Schnorr协议
  12. python内置颜色条_用于多个子图的Python颜色条
  13. 汽车UDS诊断详解及Vector相关工具链使用说明——6.1 使用DiVa进行诊断自动化测试
  14. RabbitMQ狂神说笔记(RabbitMQ B站狂神说笔记、KuangStudy、学相伴飞哥)
  15. 二手机械硬盘+硬盘盒组成移动机械硬盘【小记】
  16. WebGoat通关攻略与详细解析——SQL Injection(intro)篇
  17. 超详细Eclipse配置Tomcat配置教程
  18. #pragma warning
  19. 邓丽君逝世19周年 罕见婉约迷人私房照曝光
  20. php 插件判断手机版,laravel设备检测,区分手机端浏览器和pc端浏览器,隐藏或显示某些代码...

热门文章

  1. PHP中CURL方法curl_setopt()函数的一些参数
  2. {{jQuery源码分析}}jQuery对象初始化的多种传参数形式
  3. 在fedora20下面手动为自己的安装程序创建桌面图标
  4. hdu 1106 字符串处理
  5. 请求验证过程检测到有潜在危险的客户端输入值,对请求的处理已经中止。
  6. C++之++操作符重载
  7. C# 对话框使用整理
  8. textbox根据内容自动调整高度
  9. Unity实现IOS原生分享
  10. 【Linux】VirtualBox安装ubuntu排错LowGraphic