本文目录:

一、队列

二、生产者消费者模型

三、线程及守护线程

四、线程常用方法

五、启动线程的另一种方式

六、锁

七、锁死

八、死锁

九、单个锁能不能死锁

十、信号旗

一、队列

什么是队列

类似于链表与堆栈一样,队列也是存储数据的结构,在队列中数据进入队列的顺序很重要,一般来说,队列就是一群人或者事务安排好的顺序等待接受服务或者处理。

定义 :队列,有称之为伫列(queue),是先进先出的线性表,在具体应用中通常用链表或者数组来实现,队列只允许在后端进行插入操作,在前端(称为front)进行删除操作。

为什么需要队列

在进程彼此之间相互隔离要实现进程间的通信(IPC),multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用信息传递的。

怎么用队列

创建队列底层就是以管道和锁定的方式实现的:

 Queue([maxsize]):创建共享的进程队列,Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递。 

参数:maxsize是队列中允许最大项数,省略则无大小限制

方法介绍:

q.put方法用以插入数据到队列中,put方法还有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,该方法会阻塞timeout指定的时间,直到该队列有剩余的空间。如果超时,会抛出Queue.Full异常。如果blocked为False,但该Queue已满,会立即抛出Queue.Full异常。
q.get方法可以从队列读取并且删除一个元素。同样,get方法有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,那么在等待时间内没有取到任何元素,会抛出Queue.Empty异常。如果blocked为False,有两种情况存在,如果Queue有一个值可用,则立即返回该值,否则,如果队列为空,则立即抛出Queue.Empty异常. q.get_nowait():同q.get(False)
q.put_nowait():同q.put(False) q.empty():调用此方法时q为空则返回True,该结果不可靠,比如在返回True的过程中,如果队列中又加入了项目。
q.full():调用此方法时q已满则返回True,该结果不可靠,比如在返回True的过程中,如果队列中的项目被取走。
q.qsize():返回队列中目前项目的正确数量,结果也不可靠,理由同q.empty()和q.full()一样

  q.cancel_join_thread():不会在进程退出时自动连接后台线程。可以防止join_thread()方法阻塞
  q.close():关闭队列,防止队列中加入更多数据。调用此方法,后台线程将继续写入那些已经入队列但尚未写  入的数据,但将在此方法完成时马上关闭。如果q被垃圾收集,将调用此方法。关闭队列不会在队列使用者中产  生任何类型的数据结束信号或异常。例如,如果某个使用者正在被阻塞在get()操作上,关闭生产者中的队列不  会导致get()方法返回错误。
  q.join_thread():连接队列的后台线程。此方法用于在调用q.close()方法之后,等待所有队列项被消   耗。默认情况下,此方法由不是q的原始创建者的所有进程调用。调用q.cancel_join_thread方法可以禁止  这种行为

 
'''
multiprocessing模块支持进程间通信的两种主要形式:管道和队列
都是基于消息传递实现的,但是队列接口
''' from multiprocessing import Process,Queue
import time
q=Queue(3) #put ,get ,put_nowait,get_nowait,full,empty
q.put(3)
q.put(3)
q.put(3)
print(q.full()) #满了 print(q.get())
print(q.get())
print(q.get())
print(q.empty()) #空了

二、生产者消费者模型

什么是生产者消费者模型

在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题,该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。

生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。

基于队列实现生产者消费者模型

为什么要用生产者消费者模型

在多线程开发当中,如果生产者处理的任务过快,消费者来不及消化,那么必须等待消费者处理完,相反如果消费者消化速度快于生产者,也要等待生产者,为了达到生产与消费之间达到平衡,引入了该模型。

生产者消费者模型实例

import time,random
from multiprocessing import Process,Queue
# 制作热狗
def make_hotdog(queue,name):
for i in range(3):
time.sleep(random.randint(1,2))
print("%s 制作了一个热狗 %s" % (name,i))
# 生产得到的数据
data = "%s生产的热狗%s" % (name,i)
# 存到队列中
queue.put(data)
# 装入一个特别的数据 告诉消费方 没有了
#queue.put(None) # 吃热狗
def eat_hotdog(queue,name):
while True:
data = queue.get()
if not data:break
time.sleep(random.randint(1, 2))
print("%s 吃了%s" % (name,data)) if __name__ == '__main__':
#创建队列
q = Queue()
p1 = Process(target=make_hotdog,args=(q,"钻钻的热狗店"))
p2 = Process(target=make_hotdog, args=(q, "egon的热狗店"))
p3 = Process(target=make_hotdog, args=(q, "老王的热狗店")) c1 = Process(target=eat_hotdog, args=(q,"思聪"))
c2 = Process(target=eat_hotdog, args=(q, "李哲")) p1.start()
p2.start()
p3.start() c1.start()
c2.start() # 让主进程等三家店全都做完后....
p1.join()
p2.join()
p3.join() # 添加结束标志 注意这种方法有几个消费者就加几个None 不太合适 不清楚将来有多少消费者
q.put(None)
q.put(None) # 现在 需要知道什么时候做完热狗了 生产者不知道 消费者也不知道
# 只有队列知道 print("主进程over") # 生产方不生产了 然而消费方不知道 所以已知等待 get函数阻塞
# 三家店都放了一个空表示没热狗了 但是消费者只有两个 他们只要看见None 就认为没有了
# 于是进程也就结束了 造成一些数据没有被处理
# 等待做有店都做完热狗在放None
import time,random
from multiprocessing import Process,JoinableQueue
# 制作热狗
def make_hotdog(queue,name):
for i in range(3):
time.sleep(random.randint(1,2))
print("%s 制作了一个热狗 %s" % (name,i))
# 生产得到的数据
data = "%s生产的热狗%s" % (name,i)
# 存到队列中
queue.put(data)
# 装入一个特别的数据 告诉消费方 没有了
#queue.put(None) # 吃热狗
def eat_hotdog(queue,name):
while True:
data = queue.get()
time.sleep(random.randint(1, 2))
print("%s 吃了%s" % (name,data))
# 该函数就是用来记录一共给消费方多少数据了 就是get次数
queue.task_done() if __name__ == '__main__':
#创建队列
q = JoinableQueue()
p1 = Process(target=make_hotdog,args=(q,"邵钻钻的热狗店"))
p2 = Process(target=make_hotdog, args=(q, "egon的热狗店"))
p3 = Process(target=make_hotdog, args=(q, "老王的热狗店")) c1 = Process(target=eat_hotdog, args=(q,"思聪"))
c2 = Process(target=eat_hotdog, args=(q, "李哲")) p1.start()
p2.start()
p3.start() # 将消费者作为主进程的守护进程
c1.daemon = True
c2.daemon = True c1.start()
c2.start() # 让主进程等三家店全都做完后....
p1.join()
p2.join()
p3.join() # 如何知道生产方生产完了 并且 消费方也吃完了 # 方法一:等待做有店都做完热狗在放None
# # 添加结束标志 注意这种方法有几个消费者就加几个None 不太合适 不清楚将来有多少消费者
# q.put(None)
# q.put(None) # 主进程等到队列结束时再继续 那队列什么时候算结束? 生产者已经生产完了 并且消费者把数据全取完了
q.join() # 已经明确生产放一共有多少数据 # 现在 需要知道什么时候做完热狗了 生产者不知道 消费者也不知道
# 只有队列知道 print("主进程over")
# 生产方不生产了 然而消费方不知道 所以一直等待 get函数阻塞
# 三家店都放了一个空表示没热狗了 但是消费者只有两个 他们只要看见None 就认为没有了
# 于是进程也就结束了 造成一些数据没有被处理
#生产者消费者模型总结

    #程序中有两类角色
一类负责生产数据(生产者)
一类负责处理数据(消费者) #引入生产者消费者模型为了解决的问题是:
平衡生产者与消费者之间的工作能力,从而提高程序整体处理数据的速度 #如何实现:
生产者<-->队列<——>消费者
#生产者消费者模型实现类程序的解耦和

简单总结

三、线程及守护线程

什么是线程

是操作系统能够进行运算调度最小的单位,它被包含在进程之中,是进程中的实际运作单位,一条线程值的是进程中的一个单一顺序的控制流,一个进程并行执行不同的任务

为什么用线程

以下有多个场景需要用到多线程:

1.避免阻塞

单个线程中的程序是按顺序执行的,如果前面的操作发生阻塞,那么久会影响到后面的操作,这时候可以采用多线程

2.避免CPU空转

举例当web server为例,来了一个https请求,服务器就会处理一个请求,依次处理后续请求,CPU会有大量闲置时间,当然https请求来了,会涉及到RPC、数据库的访问、磁盘IO等操作,这些都会拖慢CPU,而在等待的时候CPU就一直等在哪里,影响到了server的性能。因此可以采用多线程时遇到IO操作,cpu就会迅速切换到其他程序处理任务,大大提高了CPU的性能。

3.提升性能

打个比方一个餐厅只有一个厨师一口灶头,来个10个不同的菜,按程序只能做十次才能结束任务,现在老板找来9个厨师,添加了9个灶台,完成10个菜所用的时间就比之前提高了90%了。

怎么用线程

from threading import Thread
import time def task():
time.sleep(5)
print("子线程...") t = Thread(target=task)
t.daemon = True # 守护线程 执行顺序与进程中一样
t.start()
print("over")
from threading import Thread

# a = 100
#
# def task():
# global a
# a = 1
# print("这是给子线程执行的任务!")
#
# # 创建一个子线程
# t = Thread(target=task)
# # 启动这个子线程
# t.start()
# print("主")
# print(a) # 在多进程中 开启子进程需要消耗大量的资源 所以主进程会先比子进程执行
# 子线程的开启速度比进程快的多
# 在多线程中 子线程可以直接访问主线程的内容
# 多个线程之间时平等的 所以不存在父子关系
# 在今后的开发中 每当出现i/o阻塞 比较耗时的操作 import time,os def task():
time.sleep(2)
print("子线程 run.....")
print(os.getpid()) t = Thread(target=task)
t.start()
# 主线程等到子线程结束
t.join()
print("over")
print(os.getpid())

线程和进程的区别

  进程是一个资源单位

  一个进程可以包含多个线程

  多个线程之间的数据可以共享

  线程开销比进程小

  在多个线程中CPU切换速度会比非常快,但资源消耗没有进程高

四、线程常用方法

from threading import Thread,current_thread,active_count,enumerate
import time def task():
print("子线程...")
time.sleep(1)
# 获取当前线程对象 非常常用
print(current_thread()) t = Thread(target=task,name="矮根线程!")
# t.daemon = True # 守护线程 执行顺序与进程中一样
print(t.name)
print(t)
t.start()
# 获取当前活跃线程的数量
print(active_count())
# 返回活跃的线程对象枚举
print(enumerate())
print("over")

五、启动线程的另一种方式

from threading import Thread,current_thread

# 写一个类继承Thread
class MyThread(Thread):
def run(self):
print("run 函数执行!")
print(current_thread()) mt = MyThread()
mt.start() print(current_thread())

六、锁

加入锁相当于给所有的线程串联起来,先拿到锁的线程执行完释放锁资源、依次类推

from threading import Thread,Lock
# 创建一个互斥锁
mutex = Lock() def task1():
# 锁定
mutex.acquire()
for i in range(100):
print("===================")
# 打开
mutex.release()
def task2():
mutex.acquire()
for i in range(100):
print("!!!!!!!!!!!!!!!!!!")
mutex.release() def task3():
mutex.acquire()
for i in range(100):
print("********************")
mutex.release() t1 = Thread(target=task1)
t2 = Thread(target=task2)
t3 = Thread(target=task3) t1.start()
t2.start()
t3.start()

七、锁死

from threading import Thread,Lock
import time,random mutex1 = Lock()
mutex2 = Lock()
def fun1():
mutex1.acquire()
print("func1抢到了锁1")
time.sleep(1)
mutex2.acquire()
print("func1抢到了锁2") mutex2.release()
print("func1释放了锁2") mutex1.release()
print("func1释放了锁1") def fun2(): mutex2.acquire()
print("func2抢到了锁2") time.sleep(1) mutex1.acquire()
print("func2抢到了锁1") mutex1.release()
print("func2释放了锁2") mutex2.release()
print("func2释放了锁1") # def fun3():
# fun1()
# fun2( t1 = Thread(target=fun1)
t1.start()
t2 = Thread(target=fun2)
t2.start()
# 以下控制台内容

D:\Python36\python.exe D:/Myproject/work_log/test2.py
func1抢到了锁1
func2抢到了锁2

# 当func1抢到锁1后,准备抢锁2,可以被func2抢到锁2,这个时候func1在等func2释放锁2的资源,反之func2在等func1释放锁1的资源,但情况是他们都在互相等对方的释放锁,却没
有一个释放,这是就出现程序卡在那里,发生锁死的情况

八、死锁

from threading import Thread,Lock,current_thread,RLock
import time
# 叉子
locka = RLock()
# 盘子
lockb = RLock() def task1():
print(current_thread())
locka.acquire()
print("抢到叉子 需要盘子")
time.sleep(0.1)
lockb.acquire()
print("吃饭") lockb.release()
locka.release() def task2():
print(current_thread())
lockb.acquire()
print("抢到盘子 需要叉子")
time.sleep(0.1)
locka.acquire() print("吃饭")
locka.release()
lockb.release() t1 = Thread(target=task1)
t1.start()
t2 = Thread(target=task2)
t2.start() # 死锁发生的条件 有多个线程 多个锁 如果只有一个锁 无论是LOCK RLOK 卡不死(前提是逻辑上没有错误)
# RLock 就算你的代码逻辑不对 同一个线程多次对一个锁执行acquire 也不会卡死

九、单个锁能不能死锁

from threading import  Thread,Lock,RLock,current_thread

l = Lock()  # 互斥锁

# l.acquire()
# print("zxxzxxxzxzx")
# l.acquire()
# print("aaaaaaaaa") # RLock 递归锁 重入锁 可以多次执行acquire # lock = RLock()
#
# lock.acquire()
# print("aaaaaaaaaaa")
# lock.acquire()
# print("bbbbbbbbbbb") import time
lock = RLock()
# 对于同一个线程而言 可以多次acquire 其他线程会被阻塞
def task():
lock.acquire()
for i in range(5):
time.sleep(1)
print(current_thread())
lock.release() Thread(target=task).start()
Thread(target=task).start() #

十、信号旗

from threading import Thread,Semaphore,current_thread,active_count

import time
# 用于控制 同时执行被锁定代码的线程数量 也就是线程的并发数量
# 也是一种锁
sm = Semaphore(1) def task():
sm.acquire()
for i in range(10):
print(current_thread())
time.sleep(0.5)
sm.release() def task2():
for i in range(10):
print(current_thread())
time.sleep(0.5) for i in range(5):
Thread(target=task).start()
Thread(target=task2).start()
print(active_count())

资料参考:

https://blog.csdn.net/Pandaminn/article/details/92682237

https://www.cnblogs.com/linhaifeng/articles/7428877.html#_label1

https://blog.csdn.net/CYTDCHE/article/details/79075013

并发编程:生产消费模型、死锁与Rlock、线程、守护线程、信号量、锁的更多相关文章

  1. Python并发编程-生产消费模型

    生产消费模型初步 #产生两个子进程,Queue可以在子进程之间传递消息 from multiprocessing import Queue,Process import random import t ...

  2. Java并发编程-Java内存模型

    JVM内存结构与Java内存模型经常会混淆在一起,本文将对Java内存模型进行详细说明,并解释Java内存模型在线程通信方面起到的作用. 我们常说的JVM内存模式指的是JVM的内存分区:而Java内存 ...

  3. x86-TSO : 适用于x86体系架构并发编程的内存模型

    Abstract : 如今大数据,云计算,分布式系统等对算力要求高的方向如火如荼.提升计算机算力的一个低成本方法是增加CPU核心,而不是提高单个硬件工作效率. 这就要求软件开发者们能准确,熟悉地运用高 ...

  4. 【并发编程】一个最简单的Java程序有多少线程?

    一个最简单的Java程序有多少线程? 通过下面程序可以计算出当前程序的线程总数. import java.lang.management.ManagementFactory; import java. ...

  5. 并发编程学习笔记(3)----synchronized关键字以及单例模式与线程安全问题

    再说synchronized关键字之前,我们首先先小小的了解一个概念-内置锁. 什么是内置锁? 在java中,每个java对象都可以用作synchronized关键字的锁,这些锁就被称为内置锁,每个对 ...

  6. C#高性能大容量SOCKET并发(六):超时Socket断开(守护线程)和心跳包

    原文:C#高性能大容量SOCKET并发(六):超时Socket断开(守护线程)和心跳包 守护线程 在服务端版Socket编程需要处理长时间没有发送数据的Socket,需要在超时多长时间后断开连接,我们 ...

  7. 子进程回收资源两种方式,僵尸进程与孤儿进程,守护进程,进程间数据隔离,进程互斥锁,队列,IPC机制,线程,守护线程,线程池,回调函数add_done_callback,TCP服务端实现并发

    子进程回收资源两种方式 - 1) join让主进程等待子进程结束,并回收子进程资源,主进程再结束并回收资源. - 2) 主进程 “正常结束” ,子进程与主进程一并被回收资源. from multipr ...

  8. 并发编程(共享模型之管程wait notify)

    本文主要讲解wait/notify的正确使用姿势.park/unpark.join()的原理.模式之生产者-消费者模式(异步).保护性暂停模式(同步).线程状态转换的流程.死锁和活锁以及如何检查死锁等 ...

  9. Java并发编程、内存模型与Volatile

    http://www.importnew.com/24082.html  volatile关键字 http://www.importnew.com/16142.html  ConcurrentHash ...

随机推荐

  1. 新股定价谁说了算?一文读懂中国IPO询价制度

    总体来说,在市场化条件下,确定股票首次公开发行的价格可以分为两个步骤:一是股票估值:选择一定的股票估值模型,对拟发行股票的公司进行估值,并初步确定发行价格和询价区间:二是发现股票市场价格,主要方式是I ...

  2. express服务端

    1. 使用 Node.js + Express 开发服务端 2. 使用 Node.js + Express+MySQL 实现简单的增删改查 3. 初识NodeJS服务端开发(Express+MySQL ...

  3. 修改注册表打开PDF内嵌的zip等文件

    今天在打开一个 PDF 文件的时候,发现文件里面嵌入的 .zip 文件无法打开.当然 .png 之类的文件还是可以打开的.网上的各种“信任管理器”白名单方法都是无效的.后来查了官网说明,得知是因为这类 ...

  4. Navicat Premium12 注册机下载及教程

    1.下载Navicat Premium 官网https://www.navicat.com.cn/下载最新版本下载安装(文末,网盘地址有64位安装包和注册机下载) 2.激活Navicat Premiu ...

  5. js-转换方式示例

    var person1 = { toLocaleString : function () { return "Baraka"; }, toString : function() { ...

  6. mysql语句(一)

    --建表CREATE TABLE IF NOT EXISTS `runoob_tbl`( `runoob_id` INT UNSIGNED AUTO_INCREMENT, `runoob_title` ...

  7. SQLite进阶-10.约束

    约束 约束是作用于数据表中列上的规则,用于限制表中数据的类型.约束的存在保证了数据库中数据的精确性和可靠性. 约束可以是列级或表级,列级约束作用于单一的列,而表级约束作用于整张数据表. SQLite中 ...

  8. H. The Nth Item(The 2019 Asia Nanchang First Round Online Programming Contest)

    题意:https://nanti.jisuanke.com/t/41355 给出N1,计算公式:A=F(N)Ni=Ni-1 ^ (A*A),F为类斐波那契需要矩阵快速幂的递推式. 求第k个N. 思路: ...

  9. python-面向对象速查表-内置方法-内置函数-内置属性(只整理了部分内容)

    今日临时总结的内容,可能还有些不正确的地方,初步当做个速查表吧. 类的内置函数(继承object的,自己重写) 内置函数 执行时机 注意点 调用案例 __init__ 实例化对象时 不允许写返回值(r ...

  10. MySQL安装及初级增删改查一

    学习MYsql 是参照这个维C果糖的总结,学习目录网址:https://blog.csdn.net/qq_35246620/article/details/70823903,谢谢大神的无私分享. 一. ...