子进程回收资源两种方式,僵尸进程与孤儿进程,守护进程,进程间数据隔离,进程互斥锁,队列,IPC机制,线程,守护线程,线程池

子进程回收资源两种方式

- 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 os

import 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 Process

import 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 = 10

def func():
    global number
    number = 100

def func2(number):
    number += 100

if __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)  # 10

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.队列: 先进先出 进----》 [3, 2, 1] ----》 出 1, 2, 3
- 先存放的数据,就先取出来。
相当于一个第三方的管道,可以存放数据。

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

from multiprocessing import Queue  # multiprocessing提供队列  先进先出
from multiprocessing import JoinableQueue  # 基于 Queue 封装的队列 先进先出
import queue  # python内置的队列  先进先出

# 第一种
Queue(5) #指的是队列中只能存放5份数据
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 time

def 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:
            break

if __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.线程

10.守护线程

'''
线程:
    1.什么是线程?
        进程: 资源单位。
        线程: 执行单位。

        线程与进程都是虚拟的概念,为了更好表达某种事物。

        注意: 开启一个进程,一定会自带一个线程,线程才是真正的执行者。


    2.为什么要使用线程?
        节省资源的占用。

        - 开启进程:
            - 1) 会产生一个内存空间,申请一块资源。
            - 2) 会自带一个主线程
            - 3) 开启子进程的速度要比开启子线程的速度慢

        - 开启线程
            - 1) 一个进程内可以开启多个线程,从进程的内存空间中申请执行单位。
            - 2) 节省资源。

        - 开启三个进程:
            - 占用三份内存资源

        - 开启三个线程:
            - 从一个内存资源中,申请三个小的执行单位

        - IO密集型用: 多线程
            - IO(时间由用户定):
                - 阻塞: 切换 + 保存状态

        - 计算密集型用: 多进程
            - 计算(时间由操作系统定):
                - 计算时间很长 ---> 切换 + 保存状态


    注意: 进程与进程之间数据是隔离的,线程与线程之间的数据是共享的。

    3.怎么使用线程?


    - 守护线程

'''
from threading import Thread
import time

number = 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_thread number = 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 = 100

def 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)  # 90

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

from concurrent.futures import ThreadPoolExecutor
import time

# pool只能创建100个线程
pool = ThreadPoolExecutor(100)


def task(line):
    print(line)
    time.sleep(10)


if __name__ == '__main__':
    for line in range(1000):
        pool.submit(task, line)

猜你喜欢

转载自www.cnblogs.com/ludingchao/p/12003438.html