死锁现象(了解)
死锁:
# 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象
'''单进程和单线程不会出现死锁现象'''
# 哲学家吃面问题
import time
from threading import Thread, Lock, RLock
# noodle_lock = Lock()
# fork_lock = Lock()
noodle_lock = fork_lock = RLock() # 递归锁,也叫可重入锁
# noodle_lock = RLock()
# fork_lock = RLock()
def eat1(name):
noodle_lock.acquire()
print('%s 抢到了面条' % name)
fork_lock.acquire()
print('%s 抢到了叉子' % name)
print('%s 吃面' % name)
fork_lock.release()
noodle_lock.release()
def eat2(name):
fork_lock.acquire()
print('%s 抢到了叉子' % name)
time.sleep(1)
noodle_lock.acquire()
print('%s 抢到了面条' % name)
print('%s 吃面' % name)
noodle_lock.release()
fork_lock.release()
for name in ['哪吒', 'jack', 'tank']:
t1 = Thread(target=eat1, args=(name,))
t2 = Thread(target=eat2, args=(name,))
t1.start()
t2.start()
线程队列
"""
同一个进程下多个线程数据是共享的
为什么先同一个进程下还会去使用队列呢
因为队列是
管道 + 锁
所以用队列还是为了保证数据的安全
"""
from multiprocessing import Queue
进程队列:Queue
import queue
线程队列:queue
"""
进程Queue用于父进程与子进程(或同一父进程中多个子进程)间数据传递
python自己的多个进程间交换数据或者与其他语言(如Java)进程queue就无能为力
queue.Queue 的缺点是它的实现涉及到多个锁和条件变量,因此可能会影响性能和内存效率。
"""
线程队列有3种:
1. 先进先出
2. 先进后出
3. 优先级队列
'''优先级队列'''
q = queue.PriorityQueue()
# put进入一个元组,元组的第一个元素是优先级(通常是数字,也可以是非数字之间的比较),数字越小优先级越高
q.put((5, 'a')) # 元组里面的第一个数字代表的是优先级,第二个元素是具体的数据
q.put((15, 'b'))
q.put((10, 'c'))
print(q.get())
print(q.get())
print(q.get())
'''
结果(数字越小优先级越高,优先级高的优先出队):
(10, 'b')
(20, 'a')
(30, 'c')
'''
进程池和线程池(掌握思路)
池:盛放更多个对象的,盛放更多的进程和线程
'''进程池和线程池都是提供的是异步调用,只需要往池子里丢任务,我们不需要等待结果,等池子里面的任务完成之后,内部做了一个回调,告诉我们执行后的结果!!!'''
# 进程池:
提前创建一个池子,这个池子里面先创建号一怼进程,然后,只需要向池子里面丢任务就行.
# 线程池:
提前创建一个池子,这个池子里面先创建号一怼线程,然后,只需要向池子里面丢任务就行.
创建池的目的:节省资源,防止内存被沾满的情况,另外就是也能提升效率,但是不能无限的开进程.
'''写个进程池来执行任务'''
def task(n, m,):
return n + m
# res=task(1, 2)
# print(res)
def callback(res):
print(res) # <Future at 0x14786aa45e0 state=finished returned int>
print(res.result()) # 3
if __name__ == '__main__':
# 1. 创建进程池
p_pool = ProcessPoolExecutor(5) # 创建一个池子,里面有5个进程
# 2. 执行任务,只需要往池子里面丢任务
# p_pool.submit(task, 1, 2)
p_pool.submit(task, n=1, m=2).add_done_callback(callback)
线程池爬取网页
import requests
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
def get_page(url):
# requests.get('http://www.baidu.com')
res = requests.get(url) # 爬取网页
name = url.rsplit('/')[-1] + '.html' # www.baidu.com.html
return {'name': name, 'text': res.content}
def call_back(fut):
print(fut.result()['name'])
with open(fut.result()['name'], 'wb') as f:
f.write(fut.result()['text'])
if __name__ == '__main__':
pool = ThreadPoolExecutor(5)
urls = ['http://www.baidu.com', 'http://www.cnblogs.com', 'http://www.taobao.com']
for url in urls:
pool.submit(get_page, url).add_done_callback(call_back)
协程
进程:资源分配的最小单位
线程:CPU执行的最小单位
协程:
# 协程的概念在操作系统中,压根不存在,操作系统中有的是进程和线程概念,他们两个都是有操作系统调度
# 协程是有我们程序员意淫出来的,它也称作是用户态的轻量级线程. 用户态---->程序员
'''协程它是单线程下的并发.'''
# 并发:
切换,保存状态
# 如何使用协程:
本质:还是来回切换(程序级别切换)
'''要想使用协程,需要借助于第三方模块:gevent模块!!!'''
1. 需要下载和安装gevent模块
pip install gevent
协程实现高并发
服务端
#######################################服务端########################################
import socket
'''猴子补丁'''
from gevent import monkey;
monkey.patch_all() # 检测IO
'''协程实现并发:其实是欺骗CPU的行为'''
# 1. 遇到io,2. 切换
'''并发:1, 遇到IO操作,会切换'''
from gevent import spawn
def talk(sock):
while True:
try:
data = sock.recv(1024)
if len(data) == 0: break
print(data)
sock.send(data + b'ly is handsome!')
except ConnectionResetError as e:
print(e)
sock.close()
break
from multiprocessing import Process
def servers():
server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen()
while True:
sock, addr = server.accept()
# p = Process(target=talk)
# p.start()
spawn(talk, sock) ### 开销是非常小的
'''协程是单线程下的并发!!!'''
g1 = spawn(servers) # 单线程下开着,,,,,单线程下的并发
g1.join()
客户端
from threading import Thread, current_thread
from socket import *
def client():
client = socket(AF_INET, SOCK_STREAM)
client.connect(('127.0.0.1', 8080))
n = 0
while True:
msg = '%s say hello %s' % (current_thread().name, n)
n += 1
client.send(msg.encode('utf-8'))
data = client.recv(1024)
print(data.decode('utf-8'))
if __name__ == '__main__':
for i in range(5000):
t = Thread(target=client)
t.start()