JoinableQueue队列,线程,线程于进程的关系,使用线程,线程的特点,守护线程,线程的互斥锁,死锁问题,递归锁,信号量
1.JoinableQueue队列
JoinableQueue([maxsize]):这就像是一个Queue对象,但是队列允许项目的使用者通知生成者项目已经被成功处理。通知进程是使用共享的信号和条件变量来实现的。
案例:
from multiprocessing import JoinableQueue
# join是等待某个任务完成 able 可以 Queue 队列
# 翻译过来被join的队列
q = JoinableQueue() q.put('')
q.put('')
print('取走一个了%s'% q.get())
q.task_done()
# 可以当作是普通的Queue的队列来使用 # task_done方法,是告诉队列这个数据已经被处理完了
# 需要注意的是,此处的处理完的并不是任务全部处理完成,
# 而指的是当前取出的数据已经处理完成。
# 所以每一个get 需要对应一个 task_done
# task_done 不能超过get的次数,否则会报多次掉用的错误
print('又取走了一个%s'% q.get())
q.task_done() # 如果队列中的值已经被取空了,再次使用get取值会卡死
# 因为他在一直在等待有人往容器里存值
# print('又取走了一个%s'% q.get())
# q.task_done() # join方法,等待队列中的数据被处理完毕,
# 一般在使用的时候,join 与 task_done 的次数 == put 的调用次数
q.join()
print('结束')
2. 线程:
什么是线程:
线程是操作系统最小的运算调度单位,被包含在进程中,一个线程就是一个固定的执行流程
3.线程于进程的关系 ***** (重点)
线程是不能单独存在的,必须存在进程中,进程是一个资源单位,其包含了运行程序所需的所有资源
线程才是真正的执行单位,没有线程,进程中的资源就无法被利用起来,所以一个进程至少包含一个线程,这个线程就称之为主线程
当我们启动一个程序时,操作系统就会自己为这个程序创建一个主线程,而由程序后期开启的线程,就称之为子线程
4.使用线程:
在使用之前,首先要想一个问题,为什么需要使用线程?
其实目的只有一个,那就是提高效率
如果把进程比喻成车间,那么线程就是车间的流水线。
我们提高效率,当然可以再造一个新车间,那需要把原材料运过去 ,这个过程是非常耗时的
所以通常情况是创建新的流水线 而不是车间
怎么使用?
使用方法和多进程是一模一样的,只是使用了不同的模块而已
模块名:threading
类名:Thread
不一样的是,开启线程是不需要放在__main__下面,因为他们共用同一个进程的数据,而进程是将所有的数据全都重新拷贝了一份
案例:
第一种,直接实例化使用
from threading import Thread # 第一种。直接调用Thread模块 def task():
print('子进程开启')
print('子进程结束') t1 = Thread(target=task) t1.start()
print('主线程结束')
第二种继承Thread,然后覆盖run方法
# 第二种,继承Thread类,覆盖run方法
class MyThread(Thread):
def run(self):
print('子线程开启')
print('子线程结束') t = MyThread()
t.start()
print('主线程结束')
5.线程的特点
1.创建开销小,不用像进程需要导入数据
2.同一个进程中的多个线程数据共享
3.多个线程之间是平等的,没有父子关系。
案例1:同一进程中的线程数据共享
# 证明同一进程中的线程共享数据 from threading import Thread # 定义一个全局变量
a = 10
def task():
global a
print('这是子线程')
a = 20 t1 = Thread(target=task) t1.start()
t1.join() print(a) # 此处的值已经被修改了,加入是在子进程中,是不会被修改的
# 因为进程之间的数据并不互通
案例二:程序线程的开销小于进程
import os, time
from threading import Thread
from multiprocessing import Process def task():
pass if __name__ == '__main__':
st_time = time.time() ts = []
for i in range(100):
# t = Thread(target=task) # 线程输出结果0.01894855499267578 (不固定,因设备而定)
t = Process(target=task) # 进程输出结果9.261330604553223 (不固定,因设备而定)
t.start()
ts.append(t)
for t in ts:
t.join() # 使用当前时间减去记录的时间会得到开启线程所用的时间
print(time.time() - st_time) print('主线程结束')
6.守护线程
一个线程可以设置为另一个线程的守护线程
特点: 被守护线程结束后守护线程也随之结束
案例:
from threading import Thread
import time def task1():
print('第一子进程开始')
time.sleep(2)
print('第一子进程结束') def task2():
print('第二子进程开始')
time.sleep(1)
print('第二子进程结束') t = Thread(target=task1)
t.daemon = Thread
t.start() t1 = Thread(target=task2)
t1.start() print('主线程结束')
# 上述案例是将 t 线程作为守护线程
# 运行顺序是:
# 第一子进程开始
# 第二子进程开始
# 主线程结束
# 第二子进程结束 # 主线程代码执行完毕后。不会立即结束,会等待其他子线程结束,主线程会等等待非守护线程 (即t2)
# 主线程会等待所有非守护线程结束后结束 # 守护线程会等到所有非守护线程结束后结束,如果守护线程已经完成任务,守护线程会立即结束
主线程代码执行完毕后。不会立即结束,会等待其他子线程结束,主线程会等等待非守护线程 (即t2)
主线程会等待所有非守护线程结束后结束
守护线程会等到所有非守护线程结束后结束,如果守护线程已经完成任务,守护线程会立即结束
7.线程的互斥锁
共享资源就意味着竞争
线程中也存在安全问题,
多线程可以并发执行,一旦并发了并且访问了同一个资源就会有问题
解决方案:还是互斥锁
案例:
from threading import Thread,enumerate,Lock
import time number = 10
lock = Lock() def task():
global number
# 使用互斥锁来锁定资源
lock.acquire()
a = number
time.sleep(0.1)
number = a - 1
# 使用完毕后释放
lock.release() for i in range(10):
t = Thread(target=task)
t.start() for t in enumerate()[1:]:
t.join()
print(number) # 只要是并发执行,在访问同一资源的时候就会出现问题,所以与进程一样
# 就需要锁来控制资源
8.死锁问题
from threading import Lock, current_thread, Thread '''
死锁问题
当程序出现了不止一把锁,分别被不同的线程持有,如果有一个资源要想使用必须同时具备两把锁
这时候程序就会进入无限卡死状态,这就称之为死锁
例如:
要吃饭,必须具备盘子和筷子,但是一个人拿着盘子等筷子。另一个人拿着筷子等盘子
如何避免死锁问题:
锁不要有多个,一个足够
如果真的发生了死锁的问题,必须迫使一方先交出锁 '''
import time lock1 = Lock() lock2 = Lock() def eat1():
lock1.acquire()
print('%s抢到了盘子' % current_thread().name)
time.sleep(0.5)
lock2.acquire()
print('%s抢到了筷子' % current_thread().name)
print('%s开动了' % current_thread().name)
lock2.release()
print('%s放下筷子' % current_thread().name) lock1.release()
print('%s放下盘子' % current_thread().name) def eat2():
lock2.acquire()
print('%s抢到了筷子' % current_thread().name)
time.sleep(0.5)
lock1.acquire()
print('%s抢到了盘子' % current_thread().name)
print('%s开动了' % current_thread().name)
lock1.release()
print('%s放下盘子' % current_thread().name) lock2.release()
print('%s放下筷子' % current_thread().name) t1 = Thread(target=eat1) t2 = Thread(target=eat2) t1.start()
t2.start()
9.递归锁
Rlock 称之为递归锁或者可重入锁
Rlock不是用来解决死锁问题的
与Lock唯一的区别: Rlock同一线程可以多次执行acquire 但是执行几次acquire就应该对应release几次
如果一个线程已经执行过acquire 其他线程将无法执行acquire
from threading import RLock,Lock,Thread
r = RLock() def task():
r.acquire()
print('子进程开始')
r.release() # 主线程锁了一次
r.acquire()
r.acquire() # 有几次acquire,就需要对应几次release
r.release()
r.release() t1 = Thread(target=task)
t1.start()
10.信号量
'''
信号量
Lock Rlock
可以限制被锁定的代码,同时可以被多少线程并发访问
Lock 锁住一个马桶,同时只能有一个人
Semaphore 锁住的是公共厕所,同时可以来一堆人
用途:仅用于控制并发访问,并不能防止并发修改造成的问题
'''
from threading import Semaphore,Thread import time # 设置每次访问的线程数量,它自带锁
s = Semaphore(5)
def task():
# 使用锁把代码锁住,让其他进程暂时无法访问资源
s.acquire()
print('子run')
time.sleep(2)
print('子over')
s.release()
for i in range(10):
t = Thread(target=task)
t.start()
总结:
多线程
线程是CPU的最小执行单位, 线程本质是一堆代码构成的执行流程
线程被包含在进程中,
进程是一个资源单位,其中存储着该程序运行所需所有资源, 可以比喻为一个车间
线程就是车间中国一条流水线,上面放的是制作产品的具体方法(就是的代码)
一个进程至少有一个线程,操作系统在运行一个程序时 会在进程中自动开启一条线程
一个进程中可以有多个线程
同一个进程中的线程共享进程内的数据
创建线程速度非常快 开销小
线程之间没有父子关系 都是平等的
使用方式和进程一致
学习多线程 多进程 都是为了提高效率 ,通过并发执行多个任务
实现并发的方式,多线程和多进程
JoinableQueue队列,线程,线程于进程的关系,使用线程,线程的特点,守护线程,线程的互斥锁,死锁问题,递归锁,信号量的更多相关文章
- 同步锁 死锁与递归锁 信号量 线程queue event事件
二个需要注意的点: 1 线程抢的是GIL锁,GIL锁相当于执行权限,拿到执行权限后才能拿到互斥锁Lock,其他线程也可以抢到GIL,但如果发现Lock任然没有被释放则阻塞,即便是拿到执行权限GIL也要 ...
- 10 并发编程-(线程)-GIL全局解释器锁&死锁与递归锁
一.GIL全局解释器锁 1.引子 在Cpython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势 首先需要明确的一点是GIL并不是Python的特性,它是在实现Pyt ...
- Thread类的其他方法,同步锁,死锁与递归锁,信号量,事件,条件,定时器,队列,Python标准模块--concurrent.futures
参考博客: https://www.cnblogs.com/xiao987334176/p/9046028.html 线程简述 什么是线程?线程是cpu调度的最小单位进程是资源分配的最小单位 进程和线 ...
- python 全栈开发,Day42(Thread类的其他方法,同步锁,死锁与递归锁,信号量,事件,条件,定时器,队列,Python标准模块--concurrent.futures)
昨日内容回顾 线程什么是线程?线程是cpu调度的最小单位进程是资源分配的最小单位 进程和线程是什么关系? 线程是在进程中的 一个执行单位 多进程 本质上开启的这个进程里就有一个线程 多线程 单纯的在当 ...
- day 33 什么是线程? 两种创建方式. 守护线程. 锁. 死锁现象. 递归锁. GIL锁
一.线程 1.进程:资源的分配单位 线程:cpu执行单位(实体) 2.线程的创建和销毁开销特别小 3.线程之间资源共享,共享的是同一个进程中的资源 4.线程之间不是隔离的 5.线程可不需 ...
- python并发编程之线程(创建线程,锁(死锁现象,递归锁),GIL锁)
什么是线程 进程:资源分配单位 线程:cpu执行单位(实体),每一个py文件中就是一个进程,一个进程中至少有一个线程 线程的两种创建方式: 一 from threading import Thread ...
- Python并发编程-进程 线程 同步锁 线程死锁和递归锁
进程是最小的资源单位,线程是最小的执行单位 一.进程 进程:就是一个程序在一个数据集上的一次动态执行过程. 进程由三部分组成: 1.程序:我们编写的程序用来描述进程要完成哪些功能以及如何完成 2.数据 ...
- Python3 进程 线程 同步锁 线程死锁和递归锁
进程是最小的资源单位,线程是最小的执行单位 一.进程 进程:就是一个程序在一个数据集上的一次动态执行过程. 进程由三部分组成: 1.程序:我们编写的程序用来描述进程要完成哪些功能以及如何完成 2.数据 ...
- day36 joinablequeue、多线程理论、多线程的两种使用方式、守护线程、互斥锁、死锁、递归锁、信号量
1.joinablequeue队列 joinablequeue与queue一样,也是一种队列,其继承自queue,也有queue中的put 与get 方法,但是在joinablequeue中有自己的 ...
- GIL全局解释器锁、死锁、递归锁、线程队列
目录 GIL全局解释锁 多线程的作用 测试计算密集型 IO密集型 死锁现象 递归锁 信号量(了解) 线程队列 GIL全局解释锁 GIL本质上是一个互斥锁. GIL是为了阻止同一个进程内多个进程同时执行 ...
随机推荐
- Vue完成页面切换中加载数据
created() { // 拿到路由传递来的car主键 let pk = this.$route.query.pk || this.$route.params.pk; // 主键不存在,就直接结束方 ...
- docker学习系列-jdk基础镜像制作
准备一台安装有docker服务的机器 1.编辑Dockerfile vim Dockerfile FROM centos:latest ADD ./jdk-8u141-linux-x64.tar. ...
- keil无法生成axf文件之解决方法
参考:参考<鱼鹰单片机>https://blog.csdn.net/weixin_42876465/article/details/88356890 其实很简单 默认情况是生成 .axf ...
- springboot2.0入门(一)----springboot 简介
一.springboot解决了什么? 避免了繁杂的xml配置,框架自动帮我们完成了相关的配置,当我们需要进行相关插件集成的时候,只需要将相关的starter通过相关的maven依赖引进,并可以进行相关 ...
- For 循环的嵌套与九九乘法表
㈠通过程序,在页面中输入如下图形 * * * * * * * * * * * * * * * * * * * * * * * * * 代码如下: //向body中输入一个内容 //document. ...
- learning armbian steps(6) ----- armbian 源码分析(一)
为了深入学习armbian,前面已经学习了如何手动构建arm ubuntu rootfs. 由于armbian官方的文档比较的匮乏,所以最终还是决定通过其编译的过程来深入地学习. 为了快速度深入地学习 ...
- ImageSharp跨平台图片处理
添加nuget引用 SixLabors.ImageSharp 和SixLabors.ImageSharp.Drawing 暂时只实现了缩略图..<pre>using SixLabors.I ...
- python 绘制f(x)=x^2
code import turtle import math turtle.speed() # 画一个平方的函数 turtle.penup() turtle., *) turtle.pendown() ...
- 6.RabbitMQ--事物
RabbitMQ之消息确认机制 如何防止消息丢失? 如何防止消息是否正确送达? 有些业务场景需要我们对于消息的幂等性要求是比较高的,需要消息不能丢失,在使用RabbitMQ的时候,我们可以通过消息持久 ...
- idea2018.3.2版本如何破解
IntelliJ IDEA2018破解教程(2019.1.11更新)破解方法:下载破解补丁→修改配置文件→输入激活码→激活成功 由于JetBrains封杀,大部分激活服务器已经不能使用,使用下面的比较 ...