欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

进程,互斥锁,生产者消费者,线程

程序员文章站 2023-09-28 23:13:48
进程,互斥锁,生产者消费者,线程 一、僵尸进程与孤儿进程 代码演示 二、子进程回收的两种方式 代码演示 三、进程守护 演示 四、进程间数据是隔离的 演示 五、进程互斥锁 演示 六、队列 演示 七、IPC(进程间通信) 演示 八、生产者与消费者 演示 子线程守护 十、线程互斥锁 演示 ......

进程,互斥锁,生产者消费者,线程

一、僵尸进程与孤儿进程

  • 代码演示
'''
僵尸进程(有坏处):
    - 在子进程结束后,主进程没有正常结束,子进程的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()

    print(f'进入主进程的io--->{current_process().pid}')
    time.sleep(4)
    print(f'进入主进程的io--->{os.getpid()}')

    #主进程结束
    print('主进程结束...')
    print(f'查看主进程{os.getpid()}')
    f = open('yafenghandsome.txt')#此时主进程没有正常结束


#僵尸进程
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(1)
    print(f'end...{os.getpid()}')
    print('子进程结束啦啦...~~~')

if __name__ == '__main__':
    p = process(target=task)

    p.start()

    print(f'进入主进程的io--->{current_process().pid}')
    time.sleep(5)
    print(f'进入主进程的io--->{os.getpid()}')

    print('主进程结束...')
    print(f'查看主主进程{os.getppid()}')
    f = open('yafeng6666.txt')

二、子进程回收的两种方式

  • 代码演示
from multiprocessing import process
import time

# def task():
#     print('start...')
#     time.sleep(2)
#     print('end...')
#
#
#
# #方式一join让主进程等待子进程结束,并回收子进程资源,主进程再结束并回收资源。
# if __name__ == '__main__':
#     p = process(target=task)
#
#     #告诉操作系统帮你开启子进程
#     p.start()
#     p.join()
#
#     time.sleep(3)
#
#     #主进程结束
#     print('主进程结束...')


# 方式二主进程 “正常结束” ,子进程与主进程一并被回收资源。

def task():
    print('start...')
    time.sleep(2)
    print('end...')


if __name__ == '__main__':
    p = process(target=task)
    p.start()

    time.sleep(1)
    print('主进程结束...')

三、进程守护

  • 演示
'''
       守护进程:
            当主进程结束时,子进程也必须结束,并回收。
'''

from multiprocessing import process

import time


def demo(name):
    print(f'start....{name}')
    time.sleep(100)
    print(f'end...{name}')
    print('子进程结束啦啊....')


if __name__ == '__main__':
    p = process(target=demo, args=('童子军jason1号',))

    # 守护进程必须在p.start()调用之前设置
    p.daemon = true  # 将子进程p设置为守护进程

    p.start()

    time.sleep(1)
    print('皇帝驾崩啦...')

四、进程间数据是隔离的

  • 演示
from multiprocessing import process
import time
'''
进程间数据是隔离的
'''

number = 10

def func():
    global number
    print('子进程1号')
    number = 100


def func2(number):
    print('子进程2号')
    number += 100


if __name__ == '__main__':
    p_obj = process(target=func)
    p_obj2 = process(target=func2, args=(number, ))

    p_obj.start()
    p_obj2.start()

    p_obj.join()
    p_obj2.join()
    time.sleep(1)

    print(f'主进程,{number}') #110  ---> 证明进程间数据不是隔离的
                  #10   ---> 证明进程间数据是隔离的

# 子进程1号
# 子进程2号
# 主进程,10

五、进程互斥锁

  • 演示
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):

    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个用户过来都可以立马查看余票
    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()


# 用户jason2查看余票,余票还剩:1!
# 用户jason0查看余票,余票还剩:1!
# 用户jason3查看余票,余票还剩:1!
# 用户jason4查看余票,余票还剩:1!
# 用户jason1查看余票,余票还剩:1!
# 用户jason5查看余票,余票还剩:1!
# 用户jason6查看余票,余票还剩:1!
# 用户jason7查看余票,余票还剩:1!
# 用户jason8查看余票,余票还剩:1!
# 用户jason2, 抢票成功!
# 用户jason0, 抢票失败!
# 用户jason3, 抢票失败!
# 用户jason4, 抢票失败!
# 用户jason1, 抢票失败!
# 用户jason5, 抢票失败!
# 用户jason6, 抢票失败!
# 用户jason7, 抢票失败!
# 用户jason8, 抢票失败!
# 用户jason9查看余票,余票还剩:0!
# 用户jason9, 抢票失败!

六、队列

  • 演示
from multiprocessing import queue #multiprocessing 提供队列,先进先出
from multiprocessing import joinablequeue  #joinablequeue提供队列,先进先出
import queue

#第一种
# q_obj1 = queue(5)  #此处的5指的是队列中只能存放5份数据
#
# #添加数据到队列中
# q_obj1.put('热巴!')
# print('添加1个啦')
# q_obj1.put('胡歌!')
# print('添加2个啦')
# q_obj1.put('亚峰!')
# print('添加3个啦')
# q_obj1.put('科比!')
# print('添加4个啦')
# q_obj1.put('詹姆斯!')
# print('添加5个啦')

# #注意:put只要队列满了,会进入阻塞状态
# q_obj1.put('sean')
# print('我想添加第六个看会不会报错')

#put_nowait:只要队列满了就会报错
# q_obj1.put_nowait('sean')


# 添加1个啦
# 添加2个啦
# 添加3个啦
# 添加4个啦
# 添加5个啦
# queue.full


#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())

# 热巴!
# 胡歌!
# 亚峰!
# 科比!
# 詹姆斯!
# _queue.empty


# #第二种方式
# q_obj1 = joinablequeue(5)  #此处的5指的是队列中只能存放5份数据
# #添加数据到队列中
# q_obj1.put('热巴!')
# print('添加1个啦')
# q_obj1.put('胡歌!')
# print('添加2个啦')
# q_obj1.put('亚峰!')
# print('添加3个啦')
# q_obj1.put('科比!')
# print('添加4个啦')
# q_obj1.put('詹姆斯!')
# print('添加5个啦')
#
# # #注意:put只要队列满了,会进入阻塞状态
# # q_obj1.put('sean')
# # print('我想添加第六个看会不会报错')
#
# # put_nowait:只要队列满了就会报错
# # q_obj1.put_nowait('sean')
#
#
# # 添加1个啦
# # 添加2个啦
# # 添加3个啦
# # 添加4个啦
# # 添加5个啦
# # queue.full
#
#
# # 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())
# #
# # 热巴!
# # 胡歌!
# # 亚峰!
# # 科比!
# # 詹姆斯!
# # _queue.empty


# 第三种方式
q_obj1 = queue.queue(5)  #此处的5指的是队列中只能存放5份数据

#添加数据到队列中
q_obj1.put('热巴!')
print('添加1个啦')
q_obj1.put('胡歌!')
print('添加2个啦')
q_obj1.put('亚峰!')
print('添加3个啦')
q_obj1.put('科比!')
print('添加4个啦')
q_obj1.put('詹姆斯!')
print('添加5个啦')

#注意:put只要队列满了,会进入阻塞状态
#q_obj1.put('sean')
#print('我想添加第六个看会不会报错')

# put_nowait:只要队列满了就会报错
#q_obj1.put_nowait('sean')


# 添加1个啦
# 添加2个啦
# 添加3个啦
# 添加4个啦
# 添加5个啦
# queue.full


#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())

# 热巴!
# 胡歌!
# 亚峰!
# 科比!
# 詹姆斯!
# _queue.empty

七、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):
    #想要在task2中获取task1中的x
    res = q.get()
    print(f'获取的数据{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()


# 添加数据
# 获取的数据100
# 9527

八、生产者与消费者

  • 演示
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()

    #创建10个生产者
    for line in range(10):
        p1 = process(target=producer, args=('yafeng', f'高级食物{line}', q))
        p1.start()


    #创建两个消费者
    c1 = process(target=customer, args=('jason', q))
    c2 = process(target=customer, args=('sean', q))
    c1.start()
    c2.start()

# yafeng生产了高级食物1食物
# yafeng生产了高级食物0食物
# yafeng生产了高级食物2食物
# yafeng生产了高级食物3食物
# yafeng生产了高级食物4食物
# yafeng生产了高级食物5食物
# yafeng生产了高级食物6食物
# yafeng生产了高级食物8食物
# yafeng生产了高级食物7食物
# yafeng生产了高级食物9食物
# jason吃了高级食物1食物
# sean吃了高级食物0食物
# jason吃了高级食物2食物
# sean吃了高级食物3食物
# jason吃了高级食物4食物
# sean吃了高级食物5食物
# jason吃了高级食物6食物
# sean吃了高级食物8食物
# jason吃了高级食物7食物
# sean吃了高级食物9食物

九、线程以及守护线程

  • 开启线程的方式以及理论知识
'''
线程:
    1、什么是线程?
        进程:资源单位
        线程:执行单位

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

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

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

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

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

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

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

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

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

    3、怎么使用线程?
    from threading import thread

'''
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)
#
# # 主进程(主线程)...
# # 100
# # start...
# # end...


# #开启线程的方式二:
# 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()

        print(f'主进程(主线程)....{current_thread().name}')
        print(number)

十、线程互斥锁

  • 演示
from threading import lock
from threading import thread
import time


#开启10个线程,对一个数据进行修改
number = 100


def task():
    global number

    lock.acquire()
    number2 = number
    time.sleep(1)
    number = number2 - 1
    lock.release()



if __name__ == '__main__':
    lock = lock()
    list1 = []
    for line in range(10):
        t = thread(target=task)
        t.start()
        list1.append(t)

    for t in list1:
        t.join()

    print(number)

#>>>> 90