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

Posted yafeng666

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了进程,互斥锁,生产者消费者,线程相关的知识,希望对你有一定的参考价值。

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

一、僵尸进程与孤儿进程

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

以上是关于进程,互斥锁,生产者消费者,线程的主要内容,如果未能解决你的问题,请参考以下文章

LinuxC线程pthread线程同步进程同步-互斥量信号量条件变量读写锁文件锁

35 守护进程 互斥锁 IPC 共享内存 的方式 生产者消费者模型

20181229(守护进程,互斥锁,IPC,生产者和消费者模型)

Python36 1.joinablequeue 2.线程理论 3.多线程对比多进程 4.线程的使用方式 4.1.产生 线程的两种方式 4.2.守护线程 4.3.线程安全问题 4.3.1.互斥锁 4

信号量与进程/线程间同步与互斥

python并发编程之多进程:互斥锁(同步锁)&进程其他属性&进程间通信(queue)&生产者消费者模型