并发编程
操作系统发展史
基于单核研究
多道技术
1.空间上的复用
多个程序公用一套计算机硬件
2.时间上的复用
切换+保存状态
例子:洗衣 烧水 做饭

切换
1.程序遇到IO操作系统会立刻剥夺走CPU的执行权限
IO:input,sleep,accept,recv...阻塞 日常生活中使用软件通常都是IO密集型

2.当你的程序长时间占用CPU的时候也会被操作系统剥夺走cpu的执行权限

进程理论
进程调度
时间片轮转法+多级反馈队列

进程三状态图
ps:程序不会立刻进入运行态 都会现在就绪态等待cpu的执行

同步异步:指的是任务的提交方式
同步:提交任务之后原地等待任务的返回结果 期间不做任何事
异步:提交任务之后立刻执行下一行代码 不等待任务的返回结果 >>> 结果的获取使用异步回调机制
阻塞非阻塞:指的是程序的运行状态
阻塞:阻塞态
非阻塞:就绪态或者是运行态

创建进程的两种方式
使用使用Process实例化
继承Process类重写run方法
ps:windows在开启进程的时候必须在__main__代码块内,因为windows是以模块导入的方式从上执行代码

什么是进程:
正在运行的程序
一个进程对应到内存中就是一块独立的内存空间

join方法
主进程等待某个指定的子进程运行结束,不影响其他子进程的运行

进程对象及其他方法
current_process().pid
os.getpid
os.getppid
terminate()
is_alive()

守护进程
daemon
这一句化必须在start之前使用

进程间数据是隔离的

互斥锁
多个程序操作用一份数据的时候会出现数据错乱的现象
如何避免:
将操作数据的部分加锁处理
会将并发变成串行牺牲了效率但是保证了数据的安全

抢锁
acquire()
释放锁
release()
抢锁步骤是由操作系统决定的,即到底谁将会被分配到锁,进而可以被执行.
只有当程序释放了锁,之后的程序才能继续去抢锁.

进程间通信IPC机制
一般进程间通信会使用队列这一数据类型来进行数据通信.
ps:

"""
队列:先进先出
堆栈:先进后出
"""
示例:
from multiprocessing import Queue

q = Queue(5) # 括号内可以传参数 表示的是这个队列的最大存储数
# 往队列中添加数据
q.put(1)
q.put(2)
# print(q.full()) # 判断队列是否满了
q.put(3)
q.put(4)
q.put(5)
# print(q.full())
# q.put(6) # 当队列满了之后 再放入数据 不会报错 会原地等待 直到队列中有数据被取走(阻塞态)

print(q.get())
print(q.get())
print(q.get())
print(q.empty()) # 判断队列中的数据是否取完
print(q.get())
print(q.get())
print(q.empty())
# print(q.get_nowait()) # 取值 没有值不等待直接报错
# print(q.get()) # 当队列中的数据被取完之后 再次获取 程序会阻塞 直到有人往队列中放入值
"""
full
get_nowait
empty
都不适用于多进程的情况
"""
from multiprocessing import Process,Queue

def producer(q):
q.put('hello GF~')

def consumer(q):
print(q.get())

if __name__ == '__main__':
q = Queue()
p = Process(target=producer,args=(q,))
c = Process(target=consumer, args=(q,))
p.start()
c.start()

"""
子进程放数据 主进程获取数据
两个子进程相互放 取数据
"""

生产者消费者模型
"""
生产者:生产/制造数据的
消费者:消费/处理数据的
这其中有时会出现下列情况
用大白话讲:做包子的,买包子的
1.做包子远比买包子的多
2.做包子的远比包子的少
供求不平衡的问题
"""
代码示例:

方法一:

使用了jionableQueue方法,

方法二:
#手动为每个进程设置等待:

from multiprocessing import Process,Queue,joinableQueue
import random , time
def producer(name, food ,q):
for i in range(4)
data = f'{name}生产了{food},第{i+1}号'
time.sleep(random.random()) # 生产间隔
q.put(data)
print(data)

def consumer (name,q):
while True:
data=q.get()
if data == None :
print('吃完了,也做完了')
break
print(f'{name}生产了{data})
time.sleep(random.random() #消化时间
q.task_done() # 告诉队列已经从队列中取出了一个数据,并且已经处理完毕

if __name__ == '__main__':
q = JoinableQueue() #创建对象

p = Process(target=producer,args=('大厨egon','馒头',q))
p1 = Process(target=producer,args=('墩子tank','生蚝',q))
# c = Process(target=consumer,args=('小小',q))
c1 = Process(target=consumer,args=('吃货',q))
p.start()
p1.start()
# c.daemon = True
c1.daemon = True
# c.start()
c1.start()
p.join()
p1.join()

q.join() # 等到队列中数据全部取出
# q.put(None)
# q.put(None)

结果如下:
墩子tank生产了生蚝.5号
吃货吃了墩子tank生产了生蚝.5号
大厨egon生产了馒头.4号
吃货吃了大厨egon生产了馒头.4号
墩子tank生产了生蚝.2号
大厨egon生产了馒头.3号
大厨egon生产了馒头.9号
吃货吃了墩子tank生产了生蚝.2号
大厨egon生产了馒头.1号
墩子tank生产了生蚝.7号
吃货吃了大厨egon生产了馒头.3号
吃货吃了大厨egon生产了馒头.9号
吃货吃了大厨egon生产了馒头.1号
墩子tank生产了生蚝.8号
吃货吃了墩子tank生产了生蚝.7号
吃货吃了墩子tank生产了生蚝.8号

Process finished with exit code 0

线程理论

什么是线程
进程线程其实都是虚拟单位,都是用来帮助我们形象的描述某种事物

进程:资源单位
线程:执行单位
将内存比如成工厂
那么进程就相当于是工厂里面的车间
而你的线程就相当于是车间里面的流水线
ps:每个进程都自带一个线程,线程才是真正的执行单位,进程只是在线程运行过程中
提供代码运行所需要的资源

为什么要有线程
开进程
1.申请内存空间 耗资源
2."拷贝代码" 耗资源

开线程
一个进程内可以起多个线程,并且线程与线程之间数据是共享的
ps:开启线程的开销要远远小于开启进程的开销

from threading import Thread
import time

def task(name):
print('%s is running'%name)
time.sleep(3)
print('%s is over'%name)
# 开线程不需要在__main__代码块内 但是习惯性的还是写在__main__代码块内
t = Thread(target=task,args=('egon',))
t.start() # 告诉操作系统开辟一个线程 线程的开销远远小于进程
# 小的代码执行完 线程就已经开启了
print('主线程')

from threading import Thread
import time

class MyThread(Thread):
def __init__(self,name):
super().__init__()
self.name = name

def run(self):
print('%s is running'%self.name)
time.sleep(3)
print('%s is over'%self.name)

t = MyThread('egon')
t.start()
print('主线程')

执行结果:
egon is running

egon is running

egon is over
egon is over

join方法

from threading import Thread,current_thread,active_count
import time
import os

def task(name,i):
print('%s is running'%name)
# print('子current_thread:',current_thread().name)
# print('子',os.getpid())
time.sleep(i)

print('%s is over'%name)
# 开线程不需要在__main__代码块内 但是习惯性的还是写在__main__代码块内
t = Thread(target=task,args=('egon',1))
t1 = Thread(target=task,args=('jason',2))
t.start() # 告诉操作系统开辟一个线程 线程的开销远远小于进程
t1.start() # 告诉操作系统开辟一个线程 线程的开销远远小于进程
t1.join() # 主线程等待子线程运行完毕
print('当前正在活跃的线程数',active_count())
# 小的代码执行完 线程就已经开启了
print('主')
# print('主current_thread:',current_thread().name)
# print('主',os.getpid())

线程间通信
注意 与进程不同的是 ,同一进程下的多线程是共用该进程下的资源的,

即他们之间使用的可以说是同一名称空间,

from threading import Thread
money = 666
def test():
global money
money = 999
t = Thread(target=task)
t.start()
t.join()
print(money)
执行结果:
999
线程对象及其他方法
from threading import Thread,current_thread
import time

守护线程:

def task(i):
print(current_thread().name)
time.sleep(i)
print('GG')
# for i in range(3):
# t = Thread(target=task,args=(i,))
# t.start()
t = Thread(target=task,args=(1,))
t.daemon = True
t.start()
print('主')
# 主线程运行结束之后需要等待子线程结束才能结束呢?
"""
主线程的结束也就意味着进程的结束
主线程必须等待其他非守护线程的结束才能结束
(意味子线程在运行的时候需要使用进程中的资源,而主线程一旦结束了资源也就销毁了)
"""

举例子:

from threading import Thread
from multiprocessing import Process
import time
def foo():
print(123)
time.sleep(1)
print("end123")

def bar():
print(456)
time.sleep(3)
print("end456")

def run():
print('789')
time.sleep(2)
print('end789')

if __name__ == '__main__':
t1=Thread(target=foo)
t2=Thread(target=bar)
t3= Thread(target=run)
t2.daemon=True
t1.start()
t2.start()
t3.start()
print("main-------开始")
执行结果:
123
456
789
main-------开始
end123
end789

可以看到 尽管我们使用了守护进程,即将主线程设置为t2的守护进程,当主线程死亡,则t2被强制杀死,那么结果就不包含'end456'.

线程互斥锁:
与操作进程互斥锁同理,当我们想要安全的增删改查数据,最后对操作数据的步骤加锁,以保证数据的安全性,操作和进程相同,即创建锁,抢锁,释放锁,

from threading import Thread,Lock
import time

n = 77

def task(mutex):
global n
mutex.acquire() # 抢锁
tmp = n
time.sleep(0.1)
n = tmp - 1
mutex.release() # 释放锁

t_list = []
mutex = Lock() # 示例化一个锁,即创建一个锁
for i in range(77):
t = Thread(target=task,args=(mutex,))
t.start()
t_list.append(t)
for t in t_list:
t.join()
print(n)

并发编程,python的进程,与线程的更多相关文章

  1. Python 3 并发编程多进程之进程与线程

    Python 3 进程与线程 进程即正在执行的一个过程.进程是对正在运行程序的一个抽象. 进程的概念起源于操作系统,是操作系统最核心的概念,也是操作系统提供的最古老也是最重要的抽象概念之一.操作系统的 ...

  2. Python3 与 C# 并发编程之~ 进程篇

      上次说了很多Linux下进程相关知识,这边不再复述,下面来说说Python的并发编程,如有错误欢迎提出- 如果遇到听不懂的可以看上一次的文章:https://www.cnblogs.com/dot ...

  3. Python3 与 C# 并发编程之~进程先导篇

      在线预览:http://github.lesschina.com/python/base/concurrency/1.并发编程-进程先导篇.html Python3 与 C# 并发编程之- 进程篇 ...

  4. java并发编程笔记(三)——线程安全性

    java并发编程笔记(三)--线程安全性 线程安全性: ​ 当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些进程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现 ...

  5. Java并发编程(您不知道的线程池操作)

    Java并发编程(您不知道的线程池操作) 这几篇博客,一直在谈线程,设想一下这个场景,如果并发的线程很多,然而每个线程如果执行的时间很多的话,这样的话,就会大量的降低系统的效率.这时候就可以采用线程池 ...

  6. Python的进程与线程--思维导图

    Python的进程与线程--思维导图

  7. 【Java并发编程】之二:线程中断

    [Java并发编程]之二:线程中断 使用interrupt()中断线程 ​ 当一个线程运行时,另一个线程可以调用对应的Thread对象的interrupt()方法来中断它,该方法只是在目标线程中设置一 ...

  8. java并发编程笔记(七)——线程池

    java并发编程笔记(七)--线程池 new Thread弊端 每次new Thread新建对象,性能差 线程缺乏统一管理,可能无限制的新建线程,相互竞争,有可能占用过多系统资源导致死机或者OOM 缺 ...

  9. java并发编程笔记(五)——线程安全策略

    java并发编程笔记(五)--线程安全策略 不可变得对象 不可变对象需要满足的条件 对象创建以后其状态就不能修改 对象所有的域都是final类型 对象是正确创建的(在对象创建期间,this引用没有逸出 ...

随机推荐

  1. 日期类之SimpleDateFormat

    1.System 类下的currentTimeMillis();2.Date类:java.util.Date及其子类java.sql.Date                  如何创建实例:其下的方 ...

  2. python的map,reduce函数与pandas的apply,filter函数

    1. python自带的apply.filter.map函数.reduce函数,很多情况下可以代替for循环: map(func,list),对list的每个元素分别执行func函数操作,显然func ...

  3. MySql Oracle SqlServer 数据库的数据类型列表

    Oracle数据类型 一.概述  在ORACLE8中定义了:标量(SCALAR).复合(COMPOSITE).引用(REFERENCE)和LOB四种数据类型,下面详细介绍它们的特性. 二.标量(SCA ...

  4. Elasticsearch到底哪点好?

    少点代码,多点头发 本文已经收录至我的GitHub,欢迎大家踊跃star 和 issues. https://github.com/midou-tech/articles 从今天开始准备给大家带来全新 ...

  5. 报错:The server cannot be started because one or more of the ports are invalid. Open the server editor and correct the invalid ports.

    今天重装eclipse和Tomcat,启动时候报标题错“The server cannot be started because one or more of the ports are invali ...

  6. ASP.NET中使用Entity Framework开发登陆注册Demo

    这里更多的是当作随身笔记使用,记录一下学到的知识,以便淡忘的时候能快速回顾 当前步骤是该项目的第一部分 第一部分(当前) 第二部分 大完结版本 直接上步骤,有类似的开发登陆注册也可以参考. 登陆注册的 ...

  7. Java 第十一届 蓝桥杯 省模拟赛 洁净数

    洁净数 小明非常不喜欢数字 2,包括那些数位上包含数字 2 的数.如果一个数的数位不包含数字 2,小明将它称为洁净数. 请问在整数 1 至 n 中,洁净数有多少个? 输入格式 输入的第一行包含一个整数 ...

  8. Java实现 LeetCode 599 两个列表的最小索引总和(使用hash提高效率)

    599. 两个列表的最小索引总和 假设Andy和Doris想在晚餐时选择一家餐厅,并且他们都有一个表示最喜爱餐厅的列表,每个餐厅的名字用字符串表示. 你需要帮助他们用最少的索引和找出他们共同喜爱的餐厅 ...

  9. Java实现 LeetCode 324 摆动排序 II

    324. 摆动排序 II 给定一个无序的数组 nums,将它重新排列成 nums[0] < nums[1] > nums[2] < nums[3]- 的顺序. 示例 1: 输入: n ...

  10. Java实现 洛谷 P1424 小鱼的航程(改进版)

    import java.util.Scanner; public class Main{ private static Scanner cin; public static int DISTENCE ...