day29_进程间的通信和线程

1、进程互斥锁

让并发变成串行,牺牲了执行效率,保证了数据安全

在程序并发执行的情况下,需要修改同一数据时使用

使用方法

# 导入模块
from multiprocessing import Lock

# 先获取Lock对象
mutex = Lock()

# 在需要上锁的方法前加锁
mutex.acquire()
# 目标方法
func()
# 在需要上锁的方法后解锁
mutex.release()

实例演示

需求:
	模拟抢票软件
    1	查看余票
    2	开始抢票
# 代码实现
import json
import time
from multiprocessing import Process
from multiprocessing import Lock


# 查看余票
def search(user):
    with open('data.json', 'r', encoding='utf-8') as fr:
        dic = json.load(fr)
    print(f'用户{user}查看余票,还剩余{dic.get("ticket_num")}...')


# 抢票
def buy(user):
    with open('data.json', 'r', encoding='utf-8') as fr:
        dic = json.load(fr)

    time.sleep(0.1)  # 模拟网络延时

    # 若有票就修改data数据
    if dic.get('ticket_num') > 0:
        dic['ticket_num'] -= 1
        with open('data.json', 'w', encoding='utf-8') as fw:
            json.dump(dic, fw)
            print(f'用户{user}抢票成功!')
    else:
        print(f'用户{user}抢票失败')

def run(user, mutex):
    search(user)

    # 添加进程互斥锁,让buy()成为一个串行的进程
    mutex.acquire()
    buy(user)
    mutex.release()


# 创建票库文件
def create_data():
    with open('data.json', 'w', encoding='utf-8') as fw:
        dic = {'ticket_num': 2}
        json.dump(dic, fw)

create_data()
if __name__ == '__main__':

    mutex = Lock()
    for i in range(10):
        p = Process(target=run, args=(i, mutex))
        p.start()

2、队列

2.1 队列的python实现

取值原则:先进先出

python实现:

模块:from multiprocessing import Queue

使用:

  • 实例化队列对象:q = Queue(max) max 参数为这个队列能存放的数据最大个数
  • 取值:按照先进先出原则一个一个的取出
    • 1、q.get() 当队列中无数据可取时,程序停在这里,等待队列中进入新的数据
    • 2、q.get_nowait() 当队列中无数据可取时,程序不会等待队列中进入新的数据,而是直接报错
  • 存值:将指定值存进队列,可以打破进程间的数据隔离特性
    • 1、q.put(value)方法,若队列已经存满了,那么使用再存值操作q.put(value)时程序将会停在这里,等待队列空余出位置后,再存值
    • 2、q.put_nowait(value),与q.put(value)不同的是,当队列满了,再使用此方法会报错
  • 查看队列是否已满:q.full() 返回值为布尔值
  • 判断队列是否为空:q.empty() 返回值为布尔值

2.2 队列用于进程间通信

进程间通信:IPC

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

3、堆栈

取值原则:先进后出

4、生产者与消费者

生产者:生产数据的

消费者:使用数据的

实例:

from multiprocessing import Queue, Process
import time


# 生产者
def producer(name, food, q):
    for i in range(10):
        data = food
        msg = f'用户{name}开始制作{data}'
        print(msg)
        q.put(data)
        time.sleep(0.1)


# 消费者
def consumer(name, q):
    while True:
        data = q.get()
        time.sleep(0.5)  # 模拟消费者使用数据时间
        print(f'用户{name}在吃{data}')


if __name__ == '__main__':
    q = Queue(5)

    # 创建生产者,即产生数据的进程对象
    p1 = Process(target=producer, args=('tank', '香香的粑粑', q))
    p2 = Process(target=producer, args=('nick', '臭臭的粑粑', q))

    # 创建消费者,即使用数据的进程对象
    c1 = Process(target=consumer, args=('egon', q))
    c2 = Process(target=consumer, args=('alex', q))

    # 启动进程
    p1.start()
    p2.start()

    # 消费者设置为守护进程
    c1.daemon = True
    c2.daemon = True
    c1.start()
    c2.start()

    p1.join()
    p2.join()

    time.sleep(4)  # 等待消费者完成任务

5、线程

5.1 什么是线程

线程与进程都是虚拟单位,目的是为了更好的 从队列中获取数据

进程:资源单位

线程:执行单位

开启一个进程,一定会有一个线程,线程才是真正的执行者

5.2 为什么使用线程

开启进程:开辟一个内存空间,占用内存资源,自带一个线程

开启线程:一个进程可以开启多个线程,线程的开销远小于 进程

注意:

  • 在python中,同一个进程下的线程不能实现并行,只能实现并发,进程可以实现并行
  • 同一进程的线程之间,数据是共享的

5.3 开启线程的两种方式

1、自定义任务

import time
from threading import Thread

def task():
    print('线程开启')
    time.sleep(1)
    print('线程结束')

# t = Thread()  # 线程对象可以放在main外面实例化,但是进程对象不行

if __name__ == '__main__':
    t = Thread(target=task)
    t.start()

2、自定义类

import time
from threading import Thread

class MyThread(Thread):
    def run(self):
        print('线程开启')
        time.sleep(1)
        print('线程结束')

t = MyThread()
t.start()

6、线程对象的属性

current_thread().name

t.isAlive()

t.is_alive()

t.getName()获取线程名

t.setName()设置线程名

t.daemon 守护线程,当线程所属进程结束运行,该线程立即结束运行

t.getpid() 获取当前线程所属的进程号

Threadding.enumerate()获取当前进程下所有的线程

from threading import Thread
from threading import current_thread
import time,os


def task():
    print(f'线程开启{current_thread().name}')
    time.sleep(3)
    print(f'线程结束{current_thread().name}')
    print(f'当前进程号:{os.getpid()}')


t1 = Thread(target=task)
t2 = Thread(target=task)

# t1.daemon = True
# t2.daemon = True
t1.start()
t2.start()

print(f'当前进程号:{os.getpid()}')

print('主进程代码执行完毕')
'''
线程开启Thread-1
线程开启Thread-2
当前进程号:12132
主进程代码执行完毕
线程结束Thread-2线程结束Thread-1

当前进程号:12132
当前进程号:12132'''

7、线程互斥锁

让并发变成串行,牺牲了执行效率,保证了数据安全

在程序并发执行的情况下,需要修改同一数据时使用

使用方法

模块:from threading import Lock

使用方法:实例化Lock对象,在线程方法上

from threading import Thread
from threading import Lock
import time

mutex = Lock()
n = 100
def task(i):
    print(f'线程{i}启动...')
    global n
    mutex.acquire()
    temp = n
    time.sleep(0.1)  # 一共等待10秒
    n = temp-1
    print(n)
    mutex.release()

if __name__ == '__main__':
    t_l=[]
    for i in range(100):
        t = Thread(target=task, args=(i, ))
        t_l.append(t)
        t.start()

    for t in t_l:
        t.join()

    # 100个线程都是在100-1
    print(n)


posted @ 2019-10-22 18:18  W文敏W  阅读(95)  评论(0)    收藏  举报