线程
进程互斥锁:
让并发变成串行, 牺牲了执行效率, 保证了数据安全.
在程序并发执行时,需要修改数据时使用.
模拟抢票软件需求:
并发查票与抢票
1.查看余票
2.开始抢票
进程互斥锁:
让并发变成串行, 牺牲了执行效率, 保证了数据安全.
在程序并发执行时,需要修改数据时使用.
import json import time from multiprocessing import Process from multiprocessing import Lock # 查看余票 def search(user): # 打开data文件查看余票 with open('data.txt', 'r', encoding='utf-8') as f: dic = json.load(f) print(f'用户{user}查看余票,还剩{dic.get("ticket_num")}...') # 开始抢票 def buy(user): # 先打开获取车票数据 with open('data.txt', 'r', encoding='utf-8') as f: dic = json.load(f) # 模拟网络延时 time.sleep(1) # 若有票,修改data数据 if dic.get("ticket_num") > 0: dic['ticket_num'] -= 1 with open('data.txt', 'w', encoding='utf-8') as f: json.dump(dic, f) print(f'用户: {user}抢票成功!') else: print(f'用户: {user}抢票失败!') # 开始抢票 def run(user, mutex): # 并发: 异步执行 search(user) # 串行: 同步执行 mutex.acquire() #加锁 buy(user) mutex.release() #释放锁 if __name__ == '__main__': # 调用Lock()类得到一个锁对象 mutex = Lock() # 同时来10个用户抢票 for i in range(10): # 并发开启10个子进程 p = Process(target=run, args=(f'用户{i}', mutex)) p.start()
队列:
相当于内存中产生一个队列空间,
可以存放多个数据,但数据的顺序是由先进去的排在前面.
堆栈:先进后出
from multiprocessing import Queue # 调用队列类,实例化队列对象 q q = Queue(5) # 若传参队列中可以存放5个数据 # q1 = Queue() # 若不传参,队列中可以存放无限大的数据,前提硬件能更得上 # put添加数据,若队列中的数据满了,则卡住 q.put(1) print('进入数据1') q.put(2) print('进入数据2') q.put(3) print('进入数据3') q.put(4) print('进入数据4') q.put(5) print('进入数据5') # 查看队列是否满了 print(q.full()) # True # 添加数据,若队列满了,则会报错 # q.put_nowait(6) # q.get(): 获取的数据遵循 "先进先出",若队列中无数据可取,也会卡住 # 取出来后,原位置就空了,此时可以再添加数据进去 print(q.get()) print(q.get()) print(q.get()) print(q.get()) print(q.get()) # print(q.get()) # get_nowait: 获取数据,队列中若没有,则会报错 # print(q.get_nowait()) # 判断队列是否为空 print(q.empty()) # False q.put(6) print('进入数据6') q.put_nowait(7) q.put_nowait(8) q.put_nowait(9) q.put_nowait(10) # print(q.get_nowait()) 获取数据,队列中若没有,则会报错 # q.put_nowait(6) 添加数据,若队列满了,则会报错 # print(q.empty()) 判断队列是否为空 # print(q.full()) 查看队列是否满了
IPC进程间通信
进程间数据是相互隔离的,若想实现进程间通信,可以利用队列.
from multiprocessing import Process from multiprocessing import Queue def test1(q): data = '数据hello' q.put(data) print('进程1开始添加数据到队列中..') def test2(q): data = q.get() print(f'进程2从队列中获取数据{data}') if __name__ == '__main__': q = Queue() p1 = Process(target=test1, args=(q, )) p2 = Process(target=test2, args=(q, )) p1.start() p2.start() print('主')
生产者与消费者
- 生产者与消费者:
生产者: 生产数据的
消费者: 使用数据的
- 生活中:
比如: 卖油条, 一边生产油条, 一边卖油条, 供需不平衡.
- 程序中:
通过队列,生产者把数据添加队列中,消费者从队列中获取数据.
from multiprocessing import Queue, Process import time # 生产者 def producer(name, food, q): # 生产名, 食物, 队列 for i in range(9): data = food, i msg = f'用户{name}开始制作{data}' print(msg) q.put(data) time.sleep(0.1) # 消费者 def consumer(name, q): while True: data = q.get() if not data: break print(f'用户{name}开始吃{data}') if __name__ == '__main__': q = Queue() # 创造生产者 p1 = Process(target=producer, args=('tank', '油条', q)) p2 = Process(target=producer, args=('华农兄弟', '竹鼠', q)) # 生产消费者 c1 = Process(target=consumer, args=('egon', q)) c2 = Process(target=consumer, args=('jason', q)) p1.start() p2.start() c1.daemon = True c2.daemon = True c1.start() c2.start() p2.join() print('主')
线程
1.什么是线程?
线程与进程都是虚拟单位,目的是为了更好地描述某种事物.
- 进程: 资源单位
- 线程: 执行单位
开启一个进程,一定会有一个线程,线程才是真正执行者.
2.为什么要使用线程?
节省内存资源.
- 开启进程:
1) 开辟一个名称空间,每开启一个进程都会占用一份内存资源.
2) 会自带一个线程
- 开启线程
1) 一个进程可以开启多个线程
2) 线程的开销远小于进程.
注意: 线程不能实现并行, 线程只能实现并发, 进程可以实现并行.
比喻: 内存就像一个工厂, 子进程就像一个工厂车间, 线程就像车间内的流水线.
开启线程方式1:
#开启线程方式1: def task(): print('线程开启') time.sleep(1) print('线程结束') # t = Thread() 也可以放在main函数外实例化,因为是在一个名称空间内,一般 放在main下面 if __name__ == '__main__': # 调用Thread线程类实例化得到线程对象 t = Thread(target=task) t.start()
开启线程方式2:
class MyThread(Thread): def run(self): print('线程开启') time.sleep(1) print('线程结束') if __name__ == '__main__': t = MyThread() t.start()
线程的属性
from threading import Thread from threading import current_thread import time def task(): print(f'线程开启{current_thread().name}') time.sleep(3) print(f'线程结束{current_thread().name}') # t = Thread() if __name__ == '__main__': # 调用Thread线程类实例化得到线程对象 # for i in range(3): # t = Thread(target=task) # t.start() # t = Thread(target=task) # print(t.isAlive()) # t.daemon = True # t.start() # print(t.isAlive()) # print(t.is_alive()) t = Thread(target=task) print(t.getName()) t.setName('111') # 设置线程名字 print(t.getName()) # 得到线程的名字 t.daemon = True t.start() t.join() print('主')
print(enumerate()) #返回一个列表,列表中包含当前执行的所有线程对象
print(activeCount()) # 获取当前执行线程的个数
# current_thread().name 线程的名字 # t.daemon = True 守护线程 # t.is_alive() 线程是否存活
# print(activeCount())获取当前执行线程的个数
# print(enumerate())返回一个列表,列表中包含当前执行的所有线程对象
线程互斥锁
线程之间数据是共享的
from threading import Thread import time x = 100 def task(): print('开启线程...') time.sleep(1) global x x = 200 print('关闭线程...') if __name__ == '__main__': t = Thread(target=task) t.start() t.join() print(x) print('主')
打印结果为:
开启线程...
关闭线程...
200
主
from threading import Thread, 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)