目录
一:进程和线程相互通信
进程之间有通信隔离,但是线程与线程之间没有,如果线程之间处理大量的数据的时候会出现资源抢占的情况
解决进程之间通信隔离方法:开辟一个公共空间
mg = multiprocessing.Manager() # 创建一个公共服务器进程 li = mg.list() # 开辟一个列表空间
import multiprocessing var = 18 def func(list1): global var var += 1 print('inner', var) list1.append(var) return var # list1.extend([1, 2, 3, 4]) if __name__ == '__main__': mg = multiprocessing.Manager() # 创建一个公共服务器进程 li = mg.list() # 开辟一个列表空间 # print(li) p1 = multiprocessing.Process(target=func, args=(li,)) p1.start() p1.join() # 等待子任务结束 # print('outer', var) # print('outer',li) for i in li: print("outer",i)
1.线程资源抢占
import threading var = 100 def func1(): global var for i in range(1000000): var += 1 def func2(): global var for i in range(1000000): var -= 1 if __name__ == '__main__': t1 = threading.Thread(target=func1) t2 = threading.Thread(target=func2) t1.start() t2.start() t1.join() t2.join() print(var)
2.解决资源抢占
互斥锁
互斥锁:对共享数据进行锁定,保证同一个时刻只能有一个线程去操作
注意:互斥锁是多个线程一起去抢,抢到锁的线程先执行,没有抢到锁的线程需要等待,等互斥锁使用完释放后,其它等待的线程再去抢这个锁
互斥锁的使用
threading模块中定义了lock变量,这个变量本质上就是一个函数,通过调用这个函数可以获得一把互斥锁
互斥锁使用步骤
# 创建锁 mutex = threading.Lock() # 上锁 mutex.acquire() # 释放锁 mutex.release()
代码实现
import threading var = 100 # 创建互斥锁 lock = threading.Lock() def func1(): global var lock.acquire() # 上锁 for i in range(1000000): var += 1 lock.release() # 释放锁 def func2(): global var lock.acquire() for i in range(1000000): var -= 1 lock.release() if __name__ == '__main__': t1 = threading.Thread(target=func1) t2 = threading.Thread(target=func2) t1.start() t2.start() t1.join() t2.join() print(var)
执行结果:
100
说明:
通过执行结果可以看出 互斥锁能够保证多个线程访问共享数据不会出现数据错误问题
小结:
-
互斥锁的作用 就是保证同一个时刻只能有一个线程去操作共享数据,保证共享数据不会出现错误问题
-
使用互斥锁的好处 确保某段关键代码只能由一个线程从头到尾完整地去执行
-
使用互斥锁会影响代码的执行效率,多任务改成了单任务执行
-
互斥锁如果没有使用好容易出现死锁的情况
死锁
死锁:一直等待对方释放锁的情景就是死锁
死锁的结果
会造成应用程序的停止响应,不能再处理其它的任务了
死锁实例代码:
import threading import time # 需求:根据下标去取值,保证同一个时刻只能有一个 线程去取值 # 创建互斥锁 lock = threading.Lock() def get_value(index): # 上锁 lock.acquire() my_list = [3, 4, 5, 6] # 0,1,2,3 # 判断下标释放越界 if index >= len(my_list): print("下标越界", index) return # 根据下标取值 value = my_list[index] print(value) time.sleep(0.2) # 释放锁 lock.release() if __name__ == '__main__': # 模拟大量线程去执行去取值的操作 for i in range(30): # 每循环一次创建一个子线程 sub_thread = threading.Thread(target=get_value, args=(i,)) # 启动线程执行任务 sub_thread.start()
避免死锁
在合适的地方释放锁
import threading import time # 需求:根据下标去取值,保证同一个时刻只能有一个 线程去取值 # 创建互斥锁 lock = threading.Lock() def get_value(index): # 上锁 lock.acquire() my_list = [3, 4, 5, 6] # 0,1,2,3 # 判断下标释放越界 if index >= len(my_list): print("下标越界", index) # 取值不成功,也需要释放互斥锁,不要影响后面的线程去取值 # 当下标越界需要释放锁,让后面的线程还可以取值 lock.release() return # 根据下标取值 value = my_list[index] print(value) time.sleep(0.2) # 释放锁 lock.release() if __name__ == '__main__': # 模拟大量线程去执行去取值的操作 for i in range(30): # 每循环一次创建一个子线程 sub_thread = threading.Thread(target=get_value, args=(i,)) # 启动线程执行任务 sub_thread.start()
小结
-
使用互斥锁的时候需要注意死锁的问题,要在合适的地方注意释放锁
-
死锁一旦产生就会造成应用程序的停止响应,应用程序无法再继续往下 执行了。
二:进程与线程的对比
1.进程和线程对比的三个方向
-
关系对比
-
区别对比
-
优缺点对比
2.关系对比
-
线程是依附在进程里面的,没有进程就没有线程
-
一个进程默认提供一条线程,进程可以创建多个线程
3.区别对比
-
进程之间不共享全局变量
-
线程之间共享全局变量,但是要注意资源竞争的问题,解决办法:互斥锁
-
创建进程的资源开销要比创建线程的资源开销要大
-
进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位
-
线程不能够独立执行,必须依存在进程中
-
多进程开发比单进程开发稳定性要强
4.优缺点对比
-
进程的优缺点:
-
优点:可以用多核
-
缺点: 资源开销大
-
-
线程的优缺点:
-
优点:资源开销小
-
缺点:不能使用多核
-
5.总结
-
进程和线程都是完成多任务的一种方式
-
多进程要比多线程消耗的资源多,但是多进程开发比单进程开发稳定性要强,某个进程挂掉不会影响其它进程
-
多进程可以使用CPU的多核运行,多线程可以共享全局变量
-
线程不能单独执行 必须依附在进程里面
对列
对列:先进先出
三:消费者与生产者模型
1.线程
import threading import queue import time import random q = queue.Queue(4) # 消费者模型 class Comsumer(threading.Thread): def __init__(self, q): super().__init__()(单继承,即只有一个父类) self.q = q # 传入队列 def run(self): # 消费者逻辑 不需要考虑生产者 只需要从队列中获取任务 while True: res = self.q.get() print(f"消费者处理了数据{res},处理结果为{res * 10}") # 生产者 class Producer(threading.Thread): def __init__(self, q): super().__init__() self.q = q # 传入队列 def run(self): # 生产者逻辑 不需要考虑消费者 只需要向队列中提交任务 while True: time.sleep(1) item = random.randint(1, 100)#生成随机数 print(f'生产者提交了任务{item}') self.q.put(item) if __name__ == '__main__': pr = Producer(q) cu = Comsumer(q) pr.start() cu.start()
2.进程
import threading import queue import time import random import multiprocessing # q = queue.Queue(4) q = multiprocessing.Queue() # 进程队列 # 消费者模型 class Comsumer(multiprocessing.Process): def __init__(self, q): super().__init__() self.q = q # 传入队列 def run(self): # 消费者逻辑 不需要考虑生产者 只需要从队列中获取任务 while True: res = self.q.get() print(f"消费者处理了数据{res},处理结果为{res * 10}") # 生产者 class Producer(multiprocessing.Process): def __init__(self, q): super().__init__() self.q = q # 传入队列 def run(self): # 生产者逻辑 不需要考虑消费者 只需要向队列中提交任务 while True: time.sleep(1) item = random.randint(1, 100) print(f'生产者提交了任务{item}') self.q.put(item) if __name__ == '__main__': pr = Producer(q) cu = Comsumer(q) pr.start() cu.start() # 两种方法:1.进程队列 2。去生产一个公共空间
注意:如果是进程去实现这个的话,那一定要把进程之间的通信隔离解决
四:并发池
1.线程池
线程池:就是在一个池子里面,有多条可以重复使用的线程
import time
import queue
import threading
class MyPool:
def __init__(self, n):
self.queue = queue.Queue(4)
for i in range(n): # 初始化时直接开启N条可以重复利用的线程
threading.Thread(target=self.func,daemon=True).start() # 保持多线程启动状态
def func(self):
while True: # 不停的接收并执行任务
# 元祖拆包
func, args, kwargs = self.queue.get() # 从队列中获取任务和参数
func(*args, **kwargs) # 执行任务
self.queue.task_done() # 队列计数器减一
# 提交任务
def submit_tasks(self, func, args=(), kwargs={}):
self.queue.put((func, args, kwargs)) # 向队列中提交任务
def join(self): # 等待所有任务在还行完毕
self.queue.join() # 当计数器为0
def func1():
time.sleep(2)
print("任务一完成")
def func2(*args, **kwargs):
time.sleep(2)
print("任务二完成", args, kwargs)
if __name__ == '__main__':
mp = MyPool(3)
mp.submit_tasks(func1)
mp.submit_tasks(func2, args=(1, 2, 'a'), kwargs={'q': 1, "w": 2})
mp.submit_tasks(func1)
mp.submit_tasks(func2, args=(3, 4, 'a'), kwargs={'q': 1, "w": 2})
mp.submit_tasks(func1)
mp.submit_tasks(func2, args=(3, 4, 'a'), kwargs={'q': 1, "w": 2})
mp.join()
2.内置池
import time
from multiprocessing.pool import ThreadPool # 导入内置线程池
from multiprocessing.pool import Pool # 导入内置进程池
def func1():
time.sleep(2)
print("任务一完成")
def func2(*args, **kwargs):
time.sleep(2)
print("任务二完成", args, kwargs)
if __name__ == '__main__':
# tp = ThreadPool(3)
tp = Pool(3)
tp.apply_async(func1)
tp.apply_async(func2, args=(1, 2), kwds={'q': 1, 'w': 2})
tp.apply_async(func1)
tp.apply_async(func2, args=(1, 2), kwds={'q': 1, 'w': 2})
tp.apply_async(func1)
tp.apply_async(func2, args=(1, 2), kwds={'q': 1, 'w': 2})
tp.close() # 停止向队列中提交任务
tp.join() # 等待现有任务执行完毕
3.使用池来实现并发服务器
服务端:
import socket
from multiprocessing.pool import ThreadPool
from multiprocessing import Pool, cpu_count
# n = cpu_count()
# print(n)
server = socket.socket()
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)
server.bind(('127.0.0.1', 9898))
server.listen(1000)
def socket_recv(conn):
while True:
recv_data = conn.recv(1024)
if recv_data:
print(recv_data)
conn.send(recv_data)
else:
conn.close()
break
def accept_rpocess(server):
tp = ThreadPool(cpu_count() * 2)
while True:
conn, addr = server.accept()
tp.apply_async(socket_recv, args=(conn,))
if __name__ == '__main__':
n = cpu_count()
pool = Pool(n)
pool.apply_async(accept_rpocess, args=(server,))
pool.close()
pool.join()
客户端
import socket
client = socket.socket()
client.connect(('127.0.0.1', 9898))
for i in range(100000):
client.send(b'ayan')
print(client.recv(1024))