threading 模块支持守护线程, 其工作方式是:守护线程一般是一个等待客户端请求服务的服务器。

如果把一个线程设置为守护线程,进程退出时不需要等待这个线程执行完成。

如果主线程准备退出时,不需要等待某些子线程完成,就可以为这些子线程设置守护线程标记。 需要在启动线程之前执行如下赋值语句: thread.daemon = True,检查线程的守护状态也只需要检查这个值即可。

整个 Python 程序将在所有非守护线程退出之后才退出, 换句话说, 就是没有剩下存活的非守护线程时才退出。 

使用thread模块

以下是三种使用 Thread 类的方法(一般使用第一个或第三个方案)

  • 创建 Thread 的实例,传给它一个函数。

import threading
from time import sleep, ctime
loops = [3, 2, 1, 1, 1]
def loop(i, nsec):
print(f'start loop {i} at: {ctime()}')
sleep(nsec)
print(f'end loop {i} at: {ctime()}')
def main():
print('start at', ctime())
threads = []
nloops = range(len(loops))
for i in nloops:
t = threading.Thread(target=loop, args=(i, loops[i]))
threads.append(t)
for i in nloops: # start threads
threads[i].start()
for i in nloops: # wait for all
threads[i].join() # threads to finish
print(f'all done at: {ctime()}')
if __name__ == '__main__':
main()

当所有线程都分配完成之后,通过调用每个线程的 start()方法让它们开始执行,而不是 在这之前就会执行。
相比于管理一组锁(分配、获取、释放、检查锁状态等)而言,这里只 需要为每个线程调用 join()方法即可。
join()方法将等待线程结束,或者在提供了超时时间的情况下,达到超时时间。
使用 join()方法要比等待锁释放的无限循环更加清晰(这也是这种锁 又称为自旋锁的原因)。

  • 创建 Thread 的实例,传给它一个可调用的类实例。

import threading
from time import sleep, ctime
# 创建 Thread 的实例,传给它一个可调用的类实例
loops = [3, 2, 1, 1, 1]
class ThreadFunc(object):
def __init__(self, func, args, name=''):
self.name = name
self.func = func
self.args = args
def __call__(self):
self.func(*self.args)
def loop(i, nsec):
print(f'start loop {i} at: {ctime()}')
sleep(nsec)
print(f'end loop {i} at: {ctime()}')
def main():
print('start at', ctime())
threads = []
nloops = range(len(loops))
for i in nloops:
t = threading.Thread(target=ThreadFunc(loop, (i, loops[i]), loop.__name__))
threads.append(t)
for i in nloops: # start threads
threads[i].start()
for i in nloops: # wait for all
threads[i].join() # threads to finish
print(f'all done at: {ctime()}')
if __name__ == '__main__':
main()
  • 派生 Thread 的子类,并创建子类的实例。

import threading
from time import sleep, ctime
# 创建 Thread 的实例,传给它一个可调用的类实例
# 子类的构造函数必须先调用其基类的构造函数
# 特殊方法__call__()在 子类中必须要写为 run()
loops = [3, 2, 1, 1, 1]
class MyThread(threading.Thread):
def __init__(self, func, args, name=''):
threading.Thread.__init__(self)
self.name = name
self.func = func
self.args = args
def run(self):
self.func(*self.args)
def loop(i, nsec):
print(f'start loop {i} at: {ctime()}')
sleep(nsec)
print(f'end loop {i} at: {ctime()}')
def main():
print('start at', ctime())
threads = []
nloops = range(len(loops))
for i in nloops:
t = MyThread(loop, (i, loops[i]), loop.__name__)
threads.append(t)
for i in nloops: # start threads
threads[i].start()
for i in nloops: # wait for all
threads[i].join() # threads to finish
print(f'all done at: {ctime()}')
if __name__ == '__main__':
main()

使用锁

python和java一样,也具有锁机制,而且创建与使用锁都是很简便的。

一般在多线程代码中,总会有一些特 定的函数或代码块不希望(或不应该)被多个线程同时执行,通常包括修改数据库、更新文件或 其他会产生竞态条件的类似情况

锁有两种状态:锁定和未锁定。而且它也只支持两个函数:获得锁和释放锁。

一般锁的调用如下

# 加载线程的锁对象
lock = threading.Lock()
# 获取锁
lock.acquire()
# ...代码
# 释放锁
lock.release()

更简洁的方法是使用with关键字,如下代码功能同上

# 加载线程的锁对象
lock = threading.Lock()
with lock :
#...代码

示例代码:

import threading
from time import sleep, ctime lock = threading.Lock() def a():
lock.acquire()
for x in range(5):
print(f'a:{str(x)}')
sleep(0.01)
lock.release() def b():
lock.acquire()
for x in range(5):
print(f'a:{str(x)}')
sleep(0.01)
lock.release() threading.Thread(target=a).start()
threading.Thread(target=b).start()

相关属性和方法

  • Thread对象的属性

属性 描述
name 线程名
ident 线程的标识符
daemon 布尔标志,表示这个线程是否是守护线程
  • Thread对象的方法

方法 描述
init(group=None, tatget=None, name=None, args=(), kwargs ={}, verbose=None, daemon=None) 实例化一个线程对象,需要有一个可调用的 target,以及其参数 args 或 kwargs。还可以传递 name 或 group 参数,不过后者还未实现。此 外 , verbose 标 志 也 是 可 接 受 的 。 而 daemon 的 值 将 会 设 定 thread.daemon 属性/标志
start() 开始执行该线程
run() 定义线程功能的方法(通常在子类中被应用开发者重写)
join (timeout=None) 直至启动的线程终止之前一直挂起;除非给出了 timeout(秒),否则 会一直阻塞
is_alive() 布尔标志,表示这个线程是否还存活
  • threading模块其他函数

函数 描述
start() 开始执行该线程
active_count() 当前活动的 Thread 对象个数
enumerate() 返回当前活动的 Thread 对象列表
settrace(func) 为所有线程设置一个 trace 函数
setprofile (func) 为所有线程设置一个 profile 函数
stack_size(size=0) 返回新创建线程的栈大小;或为后续创建的线程设定栈的大小 为 size
Lock() 加载线程的锁对象,是一个基本的锁对象,一次只能一个锁定,其余锁请求,需等待锁释放后才能获取,对象有acquire()和release()方法
RLock() 多重锁,在同一线程中可用被多次acquire。如果使用RLock,那么acquire和release必须成对出现,调用了n次acquire锁请求,则必须调用n次的release才能在线程中释放锁对象

后记

在Python多线程下,每个线程的执行方式:
1、获取GIL
2、执行代码直到sleep或者是python虚拟机将其挂起。
3、释放GIL

通常来说,多线程是一个好东西。不过由于 Python 的 GIL 的限制,多线程更适合于 I/O 密集型应用(I/O 释放了 GIL,可以允 许更多的并发),而不是计算密集型应用。对于后一种情况而言,为了实现更好的并行性,你需要使用多进程,以便让 CPU 的其他内核来执行。

请注意:多核多线程比单核多线程更差,原因是单核下多线程,每次释放GIL,唤醒的那个线程都能获取到GIL锁,所以能够无缝执行,但多核下,CPU0释放GIL后,其他CPU上的线程都会进行竞争,但GIL可能会马上又被CPU0拿到,导致其他几个CPU上被唤醒后的线程会醒着等待到切换时间后又进入待调度状态,这样会造成线程颠簸(thrashing),导致效率更低

python使用多线程的更多相关文章

  1. Python的多线程(threading)与多进程(multiprocessing )

    进程:程序的一次执行(程序载入内存,系统分配资源运行).每个进程有自己的内存空间,数据栈等,进程之间可以进行通讯,但是不能共享信息. 线程:所有的线程运行在同一个进程中,共享相同的运行环境.每个独立的 ...

  2. Python GIL 多线程机制 (C source code)

    最近阅读<Python源码剖析>对进程线程的封装解释: GIL,Global Interpreter Lock,对于python的多线程机制非常重要,其如何实现的?代码中实现如下: 指向一 ...

  3. Python实现多线程HTTP下载器

    本文将介绍使用Python编写多线程HTTP下载器,并生成.exe可执行文件. 环境:windows/Linux + Python2.7.x 单线程 在介绍多线程之前首先介绍单线程.编写单线程的思路为 ...

  4. Python实现多线程调用GDAL执行正射校正

    python实现多线程参考http://www.runoob.com/python/python-multithreading.html #!/usr/bin/env python # coding: ...

  5. Python之多线程和多进程

    一.多线程 1.顺序执行单个线程,注意要顺序执行的话,需要用join. #coding=utf-8 from threading import Thread import time def my_co ...

  6. python的多线程到底有没有用?

    在群里经常听到这样的争执,有人是虚心请教问题,有人就大放厥词因为这个说python辣鸡.而争论的核心无非就是,python的多线程在同一时刻只会有一条线程跑在CPU里面,其他线程都在睡觉.这是真的吗? ...

  7. 通过编写聊天程序来熟悉python中多线程及socket的用法

    1.引言 Python中提供了丰富的开源库,方便开发者快速就搭建好自己所需要的应用程序.本文通过编写基于tcp/ip协议的通信程序来熟悉python中socket以及多线程的使用. 2.python中 ...

  8. Python的多线程和多进程

    (1)多线程的产生并不是因为发明了多核CPU甚至现在有多个CPU+多核的硬件,也不是因为多线程CPU运行效率比单线程高.单从CPU的运行效率上考虑,单任务进程及单线程效率是最高的,因为CPU没有任何进 ...

  9. Python【多线程与多进程】

    import time,threading print("=======串行方式.并行两种方式调用run()函数=======")def run(): print('哈哈哈') # ...

  10. python学习笔记(二十九)为什么python的多线程不能利用多核CPU

    问题:为什么python的多线程不能利用多核CPU,但是咱们在写代码的时候,多线程的确是在并发,而且还比单线程快原因:因为GIL,python只有一个GIL,运行python时,就要拿到这个锁才能执行 ...

随机推荐

  1. ajax动态刷新的元素,导致绑定事件失效

    jquery事件绑定有2种方式: 1,普通事件绑定: $('元素').click(function(){}); 2, 事件代理或者叫事件委托 $('#chatPanelList').on('click ...

  2. python time库

    https://www.cnblogs.com/tkqasn/p/6001134.html

  3. 摘录<奇特的一生>1~4——[苏]格拉宁

    一 只有在不实事求是的时候,事实才会叫人感兴趣. 虚构的人物任人摆布,并且纤毫毕露--他的一切想法意图,他的过去和未来,作者都一清二楚. 我还有一个任务:向读者灌输一些有用的知识,介绍些材料. 是一个 ...

  4. [swarthmore cs75] Compiler 1 – Adder

    课程回顾 Swarthmore学院16年开的编译系统课,总共10次大作业.本随笔记录了相关的课堂笔记以及第3次大作业. 编译的过程:首先解析(parse)源代码,然后成抽象语法树(AST),再生成汇编 ...

  5. pycharm License server激活

    2018-11-15 pycharm License server激活有效:https://idea.ouyanglol.com/

  6. MySql技术内幕之MySQL入门(1)

    目录 MySql技术内幕之MySQL入门(1) 安装 关于注释 执行SQL语句 关于命令大小写 创建数据库 查看表的信息 查看更加详细的信息 查看与给定模式相匹配的列 插入数据 利用insert添加行 ...

  7. CSS3背景相关新增属性

    background-clip border-box:充满边框和内边距,内容. padding-box:充满内边距,内容 content-box:只充满内容 background-origin bor ...

  8. python猜数字GUI版本V0.1

    非常简单的GUI版猜数字游戏,后面有时间好好研究下 # -*- coding: utf-8 -*-"""Created on Mon Jan 28 16:30:17 20 ...

  9. ubuntu设置ssh登录

    1.为ubuntu添加root用户(其实Ubuntu中的root帐号默认是被禁用了的,所以登陆的时候没有这个账号),打开初始账号,输入命令:sudo passwd root,设置root的密码 2.切 ...

  10. 升讯威微信营销系统开发实践:(2)中控服务器的详细设计( 完整开源于 Github)

    GitHub:https://github.com/iccb1013/Sheng.WeixinConstruction因为个人精力时间有限,不会再对现有代码进行更新维护,不过微信接口比较稳定,经测试至 ...