进程剩余部分和线程
1创建进程的2种方式
# 创建进程的2种 方式
from multiprocessing import Process
#方式一
def task():
pass
p_obj = Process(target = task)
#告诉操作系统,让它 去创建一个子进程
p_obj.start()
#让主进程等待子进程结束后,在结束,并销毁
p_obj.join()
#方式二
class MyProcess(Process):
def run(self):
pass
p_obj = MyProcess()
#告诉操作系统,让他创建一个子进程
p_obj.start()
#让主进程 等待子进程结束后并销毁
p_obj.join()
2.子进程回收资源的2种方式
1.join让主进程等待子进程结束后,并回收子进程资源,主进程再结束并回收资源。
2.主进程“正常结束”,子进程也和主进程一并被回收
from multiprocessing import Process
import time
def task():
print('start....')
time.sleep(10)
print('end...')
if __name__ == '__main__':
obj = Process(target = task)
obj.start()
print('主进程')
time.sleep(3)
print('主程序结束')
2.1僵尸进程和孤儿进程
1.僵尸进程:(有坏处)
-在子进程结束后,主进程没有正常结束的,子进程的PID不会被回收;
缺点:
-操作系统种的PID号是有限 的,如果子进程的PID无法正常回收,则会占用PID 号;
-资源浪费
-若PID号满了,则无法创建新的进程。
2.孤儿进程(没有坏处):
-在子进程没有结束时,主进程没有正常的结束,子进程的PID不会被回收;
-操作系统的优化机制(孤儿院)
-当主程序意外终止,操作系统的会检测是否有正在运行的子进程,会把放进孤儿院中,让操作系统帮你自动回收,
PID是操作系统给进程的一个编号,是 有限的,所以在进程结束需要回收。,
3.守护进程
定义:当主进程结束后,子进程必须结束,并回收。
-守护进程必须在守护进程必须在p.start()之前设置, p.daemon = True
rom multiprocessing import Process
import time
def task():
print('start...')
time.sleep(1000)
print('end....')
if __name__ == '__main__':
p = Process(target = task)
#守护进程必须在p.start()之前设置
p.daemon = True #将子进程设置为守护进程
#告诉操作系统你开启了子进程
p.start()
time.sleep(1)
print('主进程正在进行')
>>>>>>>>>>>>>>>>>>>>>>>>>>>
start...
主进程正在进行
Process finished with exit code 0
from multiprocessing import Process
import time
def demo(name):
print(f'start...{name}')
time.sleep(1000)
print(f'end....{name}')
if __name__ == '__main__':
p = Process(target = demo,args = ('童子军json',))
#守护进程必须在p.start()之前设置
p.daemon = True #将子进程设置为守护进程
#告诉操作系统你开启了子进程
p.start()
time.sleep(1)
print('主进程正在进行')
>>>>>>>>>>>>>>>>>>>>>>>>>>
start...童子军json
主进程正在进行
Process finished with exit code 0
4.进程间的数据是隔离的
from multiprocessing import Process
import time
number = 10
def func():
global number #声明的是全局的变量 下面的number+=10 是可以用的
number +=10# 如果没有上面的global 这个是会报错
print(number)
if __name__ == '__main__':
obj = Process(target = func)
obj.start()
time.sleep(1)
print(number) ## 这个是主进程的打印结果
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
20
10
5.互斥锁
-互斥锁是一把锁,用来保证数据的读写安全。
data.json = {"number":1}
from multiprocessing import Process
from multiprocessing import Lock
import random
import time
import json
#抢票的例子
#1查看余票
def seach(name):
#读取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):
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):
#假如1000个人都可以立马查看余票
seach(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()
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>这样操作的目地是在def buy哪里加一把锁,让最先进来的人得到 以后的都不允许在进入到这一步,第一个拿到锁以后,然后会释放,第二个进来的人,可以再用。
用户[jason0]查看余票,余票还剩:[1]
用户[jason2]查看余票,余票还剩:[1]
用户[jason1]查看余票,余票还剩:[1]
用户[jason3]查看余票,余票还剩:[1]
用户[jason6]查看余票,余票还剩:[1]
用户[jason4]查看余票,余票还剩:[1]
用户[jason7]查看余票,余票还剩:[1]
用户[jason5]查看余票,余票还剩:[1]
用户[jason8]查看余票,余票还剩:[1]
用户[jason9]查看余票,余票还剩:[1]
用户[jason0],抢票成功
用户[jason2],抢票失败
用户[jason1],抢票失败
用户[jason3],抢票失败
用户[jason6],抢票失败
用户[jason4],抢票失败
用户[jason7],抢票失败
用户[jason5],抢票失败
用户[jason8],抢票失败
用户[jason9],抢票失败
6.队列
-先进先出,先存放的数据 ,就先取出来,相当于一个第三方的管道,可以存放数据
-应用:可以让进程之间的数据进行交互
from multiprocessing import Queue #提供对列,先进先出
from multiprocessing import JoinableQueue #基于Queue封装的队列,先进先出
import queue #python内置的队列 先进先出
#第一种Queue
#Queue(5)指的是队列中只能存放4个数据
q_obj1 = Queue(4)
q_obj1.put('jsaon')
print('1')
q_obj1.put('abd')Queue
print('1')
q_obj1.put('dbdf')
print('1')
q_obj1.put('qwr')
print('1')
q_obj1.put('sean') # 只要队列满了 就会进入堵塞
print('1')
>>>>>>>>>>>>>>>>>>>> #下面的这个只有4个1 打印 还有一个因为队列满了 就没有打印 但是也不会报错
1
1
1
1
q_obj1.put_nowait('sean')###用这个put_nowait 如果满了,是可以报错的
print('1')
#get:只要队列中有数据,就能获得数据,若没有则会进入阻塞
print(q_obj1.get())
print(q_obj1.get())
print(q_obj1.get())
print(q_obj1.get())
print(q_obj1.get()) ##实际只打印了4 个 还有一个没有打印 但是也不会报错
>>>>>>>>>>>>>>>>>>>>>>>>>>>
jsaon
abd
dbdf
qwr
#get_nowait:如果对列中没有数据,则会报错
print(q_obj1.get_nowait())
针对于put.nowait和get.nowait 针对第二种和第三种都是适用的
第二种
q_obj1 = JoinableQueue(4)
q_obj1.put('jsaon')
print('1')
q_obj1.put('abd')
print('1')
q_obj1.put('dbdf')
print('1')
q_obj1.put('qwr')
print('1')
print(q_obj1.get())
print(q_obj1.get())
print(q_obj1.get())
print(q_obj1.get())
print(q_obj1.get())
>>>>>>>>>>>>>>>>>>>>>>>
1
1
1
1
jsaon
abd
dbdf
qwr
第三种
q_obj1 = queue.Queue(4)
q_obj1.put('jsaon')
print('1')
q_obj1.put('abd')
print('1')
q_obj1.put('dbdf')
print('1')
q_obj1.put('qwr')
print('1')
print(q_obj1.get())
print(q_obj1.get())
print(q_obj1.get())
print(q_obj1.get())
print(q_obj1.get())
7.进程间实现通信(IPC机制)
from multiprocessing import Process
from multiprocessing import JoinableQueue
import time
def task1(q):
x = 100
q.put(x)
print('添加数据')
time.sleep(3)
print(q.get())##因为有时间的延迟 ,所以这个队列的接收的数据会延后
def task2(q):
res = q.get()
print(f'获取的数据是{res}')####这个一步先获取数据
q.put(9527)
if __name__ == '__main__':
#产生列队
q= JoinableQueue(10)
#产生2个不同子进程
p1 = Process(target = task1,args = (q,))
p2 = Process(target = task2,args = (q,))
p1.start()
p2.start()
print('主进程进行中')
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
主进程进行中
添加数据
获取的数据是100
9527
8.生产者和消费者
-生产者:生产数据
-消费者:使用数据
-出现的问题:
生产的数据跟不上使用数据的人
使用数据的速度跟不上使用数据的速度
下面的这个例子就是根据队列来实现:解决供需不平衡的问题
from multiprocessing import JoinableQueue
from multiprocessing import Process
import time
import random
#生产者 生产数据---->队列
def producer(name,food,q):
msg = f'{name}生产了{food}食物'
#生产一个食物就添加到队列中
q.put(food)
print(msg)
#消费者,使用数据
def customer(name,q):
while True:
try:
time.sleep(random.randint(1,2))
food = q.get_nowait()#若报错,则跳出循环
msg = f'{name}吃了{food}食物'
print(msg)
except Exception:
break
if __name__ == '__main__':
q = JoinableQueue()
#创建2 个生产者
for line in range(10):
p1 = Process(target = producer,args =('tank',f'pig饲料{line}',q))
p1.start()
#创建2个消费者
c1 = Process(target = customer,args = ('jason',q))
c2 = Process(target = customer,args = ('sean',q))
c1.start()
c2.start()
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
tank生产了pig饲料1食物
tank生产了pig饲料0食物
tank生产了pig饲料4食物
tank生产了pig饲料2食物
tank生产了pig饲料3食物
tank生产了pig饲料5食物
tank生产了pig饲料6食物
tank生产了pig饲料8食物
tank生产了pig饲料7食物
tank生产了pig饲料9食物
sean吃了pig饲料1食物
jason吃了pig饲料0食物
sean吃了pig饲料4食物
sean吃了pig饲料2食物
jason吃了pig饲料3食物
jason吃了pig饲料5食物
sean吃了pig饲料6食物
sean吃了pig饲料8食物
sean吃了pig饲料7食物
jason吃了pig饲料9食物
9.线程
1.什么是线程?
进程:资源单位
线程:执行单位
线程和进程都是虚拟的概念,为了更好表达某种事物
注意:开启一个进程,一定会自带一个线程,线程才是真正的执行者
2.为什么使用线程?
节约资源的占用
-开启进程:
1.会产生一个内存空间,申请一块资源
2.会自带一个主线程
3.开启子进程的速度要比开启子线程的速度慢
-开启线程
1.一个进程可以开启多个线程,从进程的内存空间中申请执行单位;
2.节约资源
-开启三个进程:
-占用三分内存资源
-开启三个线程
-从一个内存资源中,申请三个小的执行单位
-IO密集型 多线程
-计算密集型 多进程
注意:进程和进程之间的数据是隔离的,线程和线程之间的数据是共享的
from threading import Thread
import time
#启动线程的方式一
#任务一
number = 1000
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)
>>>>>>>>>>>>>>>>>>>>
start...
主进程(主线程)
100
end...
from threading import Thread
from multiprocessing import Process
import os
def work():
print('hello',os.getpid())
if __name__ == '__main__':
#主进程下下面开启的多个线程,每个线程都革命主进程的pid一样
t1 = Thread(target = work)
t2 = Thread(target = work)
t1.start()
t2.start()
print('主线程pid',os.getpid())
#开多个进程 每个进程有不同的pid
p1 = Process(target = work)
p2 = Process(target = work)
p1.start()
p2.start()
print('主进程pid',os.getpid())
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
hello 208580
hello 208580
主线程pid 208580
主进程pid 208580
hello 208688
hello 68828
9.3线程的守护进程
#启动线程的方式二
from threading import Thread
import time
class MyThread(Thread):
def run(self):
print('start...')
time.sleep(2)
print('end...')
if __name__ == '__main__':
t = MyThread()
t.daemon = True #当主进程结束后,子进程必须结束,并回收。
t.start()
print('主进程(主线程)...')
>>>>>>>>>>>>>>>>>>>>>>>>>>>
start...
主进程(主线程)...
Process finished with exit code 0
#启动线程的方式二
from threading import current_thread
from threading import Thread
import time
def task():
print(f'start...{current_thread().name}') #打印线程的名字
time.sleep(2)
print(f'end...{current_thread().name}')
if __name__ == '__main__':
for line in range(10):
t = Thread(target = task)
t.daemon = True #当主进程结束后,子进程必须结束,并回收。
t.start()
print(f'主进程(主线程)...{current_thread().name}')
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
start...Thread-1
主进程(主线程)...MainThread
start...Thread-2
主进程(主线程)...MainThread
start...Thread-3
主进程(主线程)...MainThread
start...Thread-4
主进程(主线程)...MainThread
start...Thread-5
主进程(主线程)...MainThread
start...Thread-6
主进程(主线程)...MainThread
start...Thread-7
主进程(主线程)...MainThread
start...Thread-8
主进程(主线程)...MainThread
start...Thread-9
主进程(主线程)...MainThread
start...Thread-10
主进程(主线程)...MainThread
Process finished with exit code 0
9.4线程的互斥锁
from threading import Lock
from threading import Thread
import time
lock = Lock()
number = 100
#开启10个线程 对一个数据进行修改
def task():
global number
lock.acquire()##这个是加锁
number -= 1
time.sleep(1)
lock.release()###这个是去锁
if __name__ == '__main__':
list = []
for line in range(4):
t = Thread(target = task)
t.start()
list.append(t)
for t in list:
t.join()
print(number)
>>>>>>>>>>>>>>>>>>>>>>>>>>>>
94
9.5线程池
-线程池是用来限制创建的线程数
from concurrent.futures import ThreadPoolExecutor
import time
#pool只能创建100个线程
pool = ThreadPoolExecutor(100)
def task(line):
a = time.time()
print(line)
time.sleep(10)
b = time.time()
print(b -a)
if __name__ == '__main__':
for line in range(1000):
pool.submit(task,line)