首页 技术 正文
技术 2022年11月14日
0 收藏 933 点赞 4,961 浏览 8933 个字

子进程回收资源两种方式

– 1) join让主进程等待子进程结束,并回收子进程资源,主进程再结束并回收资源。
– 2) 主进程 “正常结束” ,子进程与主进程一并被回收资源。

from multiprocessing import Process
import time# 任务
def task():
print('start....')
time.sleep(2)
print('end......')if __name__ == '__main__':
p = Process(target=task) # 告诉操作系统帮你开启子进程
p.start()
# p.join() time.sleep(3) # 主进程结束
print('主进程结束....')

2.僵尸进程与孤儿进程(了解)

'''
僵尸进程 (有坏处):
- 在子进程结束后,主进程没有正常结束, 子进程PID不会被回收。 缺点:
- 操作系统中的PID号是有限的,如有子进程PID号无法正常回收,则会占用PID号。
- 资源浪费。
- 若PID号满了,则无法创建新的进程。孤儿进程(没有坏处): - 在子进程没有结束时,主进程没有“正常结束”, 子进程PID不会被回收。
- 操作系统优化机制(孤儿院):
当主进程意外终止,操作系统会检测是否有正在运行的子进程,会他们放入孤儿院中,让操作系统帮你自动回收。# 进程的其他属性与方法
'''from multiprocessing import Process
from multiprocessing import current_process
# 在子进程中调用,可以拿到子进程对象.pid可以pid号
# 在主进程中调用,可以拿到主进程对象.pid可以pid号
import osimport time# 任务
def task():
print(f'start....{current_process().pid}')
time.sleep(1000)
print(f'end......{os.getpid()}')
print('子进程结束啦啊....~~~')if __name__ == '__main__':
p = Process(target=task) # 告诉操作系统帮你开启子进程
p.start()
# p.join() print(f'进入主进程的IO-->{current_process().pid}')
time.sleep(4)
print(f'进入主进程的IO-->{os.getpid()}')
# 主进程结束
print('主进程结束....')
print(f'查看主主进程{os.getppid()}')
f = open('tank.txt') # 强制让主进程报错

3.守护进程:
当主进程结束时,子进程也必须结束,并回收。

from multiprocessing import Processimport time# 任务
def demo(name):
print(f'start....{name}')
time.sleep(1000)
print(f'end......{name}')
print('子进程结束啦啊....~~~')if __name__ == '__main__':
p = Process(target=demo, args=('童子军jason1号', )) # 守护进程必须在p.start()调用之前设置
p.daemon = True # 将子进程p设置为守护进程 # 告诉操作系统帮你开启子进程
p.start()
# p.join() time.sleep(1)
print('皇帝驾崩啦啊~~~')

4.进程间数据是隔离的

from multiprocessing import Process
import time
'''
进程间数据是隔离。
'''
number = 10def func():
global number
number = 100def func2(number):
number += 100if __name__ == '__main__':
p_obj = Process(target=func)
p_obj2 = Process(target=func2, args=(number, ))
p_obj.start()
p_obj2.start()
p_obj2.join()
p_obj.join()
time.sleep(1)
print(number) #

5.进程互斥锁

互斥锁是一把锁,用来保证数据读写安全的。
– 抢票例子

from multiprocessing import Process
from multiprocessing import Lock # ---》 进程互斥锁
import random
import time
import json
# 抢票例子:# 1.查看余票
def search(name):
# 1.读取data.json文件中的数据
with open('data.json', 'r', encoding='utf-8') as f:
data_dic = json.load(f)
print(f'用户【{name}】查看余票,余票还剩: {data_dic.get("number")}!')# 2.若有余票,购买成功,票数会减少
def buy(name): # buy() # 网络延时
with open('data.json', 'r', encoding='utf-8') as f:
data_dic = json.load(f) # 进入这一步证明最先抢到票
if data_dic.get('number') > 0:
data_dic['number'] -= 1
time.sleep(random.randint(1, 3))
with open('data.json', 'w', encoding='utf-8') as f:
json.dump(data_dic, f)
print(f'用户【{name}】, 抢票成功!') else:
print(f'用户【{name}】, 抢票失败!')def run(name, lock):
# 1.假设1000个用户过来都可以立马查看余票
search(name) lock.acquire() # 加锁
buy(name)
lock.release() # 释放锁if __name__ == '__main__':
lock = Lock()
# 开启多进程: 实现并发
for line in range(10):
p_obj = Process(target=run, args=(f'jason{line}', lock))
p_obj.start()

6.队列: (FIFO)先进先出 进—-》 [3, 2, 1] —-》 出 1, 2, 3
– 先存放的数据,就先取出来。
相当于一个第三方的管道,可以存放数据。

附:堆栈(FILO)

应用: 让进程之间数据进行交互。

from multiprocessing import Queue  # multiprocessing提供队列  先进先出
from multiprocessing import JoinableQueue # 基于 Queue 封装的队列 先进先出
import queue # python内置的队列 先进先出# 第一种
Queue(5) #指的是队列中只能存放5份数据 如果像q=Queue(),括号内没有指定最大接受消息数量,或者数量为负数,代表可接受的消息数量没有上限,知道内存尽头
q_obj1 = Queue(5) # q_obj1队列对象
# 添加数据到队列中
q_obj1.put('jason')
print('添加1个')
q_obj1.put('hcy')
print('添加1个')
q_obj1.put('hb')
print('添加1个')
q_obj1.put('zsb')
print('添加1个')
q_obj1.put('lh')
print('添加1个')
#
# put: 只要队列满了,会进入阻塞
# q_obj1.put('sean')
# print('sean into ')# put_nowait: 只要队列满了,就会报错
# q_obj1.put_nowait('sean')# get: 只要队列中有数据,就能获取数据,若没有则会进入阻塞
print(q_obj1.get())
print(q_obj1.get())
print(q_obj1.get())
print(q_obj1.get())
print(q_obj1.get())
# print(q_obj1.get())
# get_nowait: 若队列中没有数据获取则会报错
# print(q_obj1.get_nowait())# 第二种
q_obj1 = JoinableQueue(5) # q_obj1队列对象
# 添加数据到队列中
q_obj1.put('jason')
print('添加1个')
q_obj1.put('hcy')
print('添加1个')
q_obj1.put('hb')
print('添加1个')
q_obj1.put('zsb')
print('添加1个')
q_obj1.put('lh')
print('添加1个')# put: 只要队列满了,会进入阻塞
# q_obj1.put('sean')
# print('sean into ')# put_nowait: 只要队列满了,就会报错
# q_obj1.put_nowait('sean')# get: 只要队列中有数据,就能获取数据,若没有则会进入阻塞
print(q_obj1.get())
print(q_obj1.get())
print(q_obj1.get())
print(q_obj1.get())
print(q_obj1.get())# 第三种
q_obj1 = queue.Queue(5) # q_obj1队列对象
# 添加数据到队列中
q_obj1.put('jason')
print('添加1个')
q_obj1.put('hcy')
print('添加1个')
q_obj1.put('hb')
print('添加1个')
q_obj1.put('zsb')
print('添加1个')
q_obj1.put('lh')
print('添加1个')# put: 只要队列满了,会进入阻塞
# q_obj1.put('sean')
# print('sean into ')# put_nowait: 只要队列满了,就会报错
# q_obj1.put_nowait('sean')# get: 只要队列中有数据,就能获取数据,若没有则会进入阻塞
print(q_obj1.get())
print(q_obj1.get())
print(q_obj1.get())
print(q_obj1.get())
print(q_obj1.get())

7.IPC机制 (进程间实现通信)  面试问: 什么是IPC机制?

进程间数据是相互隔离的,若想实现进程间通信,可以利用队列.

from multiprocessing import Process
from multiprocessing import JoinableQueue
import timedef task1(q):
x = 100
q.put(x)
print('添加数据') time.sleep(3)
print('p1获取数据:',q.get())def task2(q):
# 想要在task2中获取task1的x
res = q.get()
print(f'p2获取的数据是{res}')
q.put(9527)if __name__ == '__main__':
# 产生队列
q=JoinableQueue(10)
# 产生两个不同的子进程
p1 = Process(target=task1,args=(q,))
p2 = Process(target=task2,args=(q,))
p1.start()
p2.start()

8.生产者与消费者
– 生产者: 生产数据的
– 消费者: 使用数据的

– 生产油条的有人总比吃油条的人少 —> 生产数据跟不上 使用数据的人 —》 供需不平衡
– 吃油条的人比生产的油条要少 —> 使用数据的速度 跟不上 生产数据的速度

– 通过队列来实现,解决供需不平衡问题

from multiprocessing import JoinableQueue
from multiprocessing import Process
import time# 生产者: 生产数据 ---》 队列
def producer(name, food, q):
msg = f'{name} 生产了 {food} 食物'
# 生产一个食物,添加到队列中
q.put(food)
print(msg)# 消费者: 使用数据 《--- 列队
def customer(name, q):
while True:
try:
time.sleep(0.5)
# 若报错,则跳出循环
food = q.get_nowait()
msg = f'{name} 吃了 {food} 食物!'
print(msg) except Exception:
breakif __name__ == '__main__':
q = JoinableQueue() # 创建两个生产者
for line in range(10):
p1 = Process(target=producer, args=('tank1', f'Pig饲料{line}', q))
p1.start() # 创建两个消费者
c1 = Process(target=customer, args=('jason', q))
c2 = Process(target=customer, args=('sean', q))
c1.start()
c2.start()

9.线程

线程不需要像进程,使用if __name__ == ‘__main__’:

10.守护线程

'''
线程:
1.什么是线程?
进程: 资源单位。
线程: 执行单位。 线程与进程都是虚拟的概念,为了更好表达某种事物。 注意: 开启一个进程,一定会自带一个线程,线程才是真正的执行者。 2.为什么要使用线程?
节省资源的占用。 - 开启进程:
- 1) 会产生一个内存空间,申请一块资源。
- 2) 会自带一个主线程
- 3) 开启子进程的速度要比开启子线程的速度慢 - 开启线程
- 1) 一个进程内可以开启多个线程,从进程的内存空间中申请执行单位。
- 2) 节省资源。 - 开启三个进程:
- 占用三份内存资源 - 开启三个线程:
- 从一个内存资源中,申请三个小的执行单位 - IO密集型用(多核): 多线程
- IO(时间由用户定):
- 阻塞: 切换 + 保存状态 - 计算密集型用(多核): 多进程
- 计算(时间由操作系统定):
- 计算时间很长 ---> 切换 + 保存状态 注意: 进程与进程之间数据是隔离的,线程与线程之间的数据是共享的。 3.怎么使用线程? - 守护线程'''
from threading import Thread
import timenumber = 1000# 启动线程的方式一:
# 任务1:
def task():
global number
number = 100
print('start...')
time.sleep(1)
print('end...')if __name__ == '__main__':
# 开启一个子线程
t = Thread(target=task)
t.start()
# t.join()
print('主进程(主线程)...')
print(number)# 启动线程的方式二:
class MyThread(Thread):
def run(self):
print('start...')
time.sleep(1)
print('end...')if __name__ == '__main__':
# 开启一个子线程
t = MyThread()
t.start()
# t.join()
print('主进程(主线程)...')

# 守护线程
from threading import current_threadnumber = 1000
def task():
global number
number = 100
print(f'start...{current_thread().name}')
time.sleep(3)
print(f'end...{current_thread().name}')if __name__ == '__main__':
# 开启一个子线程
for line in range(10):
t = Thread(target=task)
# 加上守护线程: 主进程结束,代表主线程也结束,子线程有可能未被回收。
t.daemon = True
t.start() # t.join()
print(f'主进程(主线程)...{current_thread().name}')
print(number)

线程互斥锁

from threading import Lock
from threading import Thread
import time
lock = Lock()# 开启10个线程,对一个数据进行修改
number = 100def task():
global number # lock.acquire()
number2 = number
# time.sleep(1)
number = number2 - 1
# lock.release()if __name__ == '__main__': list1 = []
for line in range(100000000):
t = Thread(target=task)
t.start()
list1.append(t) for t in list1:
t.join() print(number) #

threading.local

# 为每个线程创建一个独立的空间,是的线程对自己的空间中的数据进行操作(数据隔离)
import threading
from threading import local
import timeobj = local()def task(i):
obj.xxxxx = i
time.sleep(2)
print(obj.xxxxx)for i in range(10): # 开启10个线程
t = threading.Thread(target=task,args=(i,))
t.start()

11.线程池
– 线程池是用来限制 创建的线程数

from concurrent.futures import ThreadPoolExecutor
import time# pool只能创建10个线程
pool = ThreadPoolExecutor(10)  # 只能同时运行10个线程def task(line):
print(line)
time.sleep(10)if __name__ == '__main__':
for line in range(100):
pool.submit(task, line)

回调函数 add_done_callback

from concurrent.futures import ThreadPoolExecutor
# pool线程池对象
pool = ThreadPoolExecutor(50)# 并发执行的任务
def task():
return '刺客伍六七'# 自定义的回调函数
def callback(res_obj): # res_obj {'result()': '刺客伍六七'}
result = res_obj.result() # 刺客伍六七# submit(函数名, 函数接收的参数1, 参数2...) ===== Thread(target=函数名, args=(函数接收的参数1, 参数2...))
# pool.submit(函数名).add_done_callback(回调函数的名字)pool.submit(task).add_done_callback(callback)
# add_done_callback
from concurrent.futures import ThreadPoolExecutor
import time
# 池子对象: 内部可以帮你提交50个启动进程的任务
p_pool = ThreadPoolExecutor(50)def task1(n):
print(f'from task1...{n}')
time.sleep(5)
return 'tank'def get_result(obj):
# print(obj.__dict__)
# print(obj._result)
result = obj.result()
print(result)if __name__ == '__main__':
n = 1
while True:
# 参数1: 函数名
# 参数2: 函数的参数1
# 参数3: 函数的参数2
# submit(参数1, 参数2, 参数3)
# add_done_callback(参数1),会将submit提交的task1执行的结果,传给get_result中的第一个参数,第一个参数是一个对象。
p_pool.submit(task1, n).add_done_callback(get_result)
n += 1

TCP服务端实现并发

#server
import socket
from concurrent.futures import ThreadPoolExecutorserver = socket.socket()server.bind(
('127.0.0.1', 9000)
)server.listen(5)# 1.封装成一个函数
def run(conn):
while True:
try:
data = conn.recv(1024)
if len(data) == 0:
break
print(data.decode('utf-8'))
conn.send(''.encode('utf-8')) except Exception as e:
break conn.close()if __name__ == '__main__':
print('Server is run....')
pool = ThreadPoolExecutor(50)
while True:
conn, addr = server.accept()
print(addr)
pool.submit(run, conn)
#client
import socketclient = socket.socket()client.connect(
('127.0.0.1', 9000)
)print('Client is run....')
while True:
msg = input('客户端>>:').encode('utf-8')
client.send(msg) data = client.recv(1024)
print(data)
相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,104
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,580
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,428
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,200
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,835
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,918