- 线程进程介绍
1. 工作最小单元是线程
2. 应用程序 -> 至少有一个进程 -> 至少有一个线程
3. 应用场景:
IO密集型:线程
计算密集型:进程
4. GIL,全局解释器锁。
- 保证同一个进程中只有一个线程同时被调度
- 线程
1. 基本使用
def task(arg):
time.sleep(arg)
print(arg)
for i in range(5):
t = threading.Thread(target=task,args=[i,])
# t.setDaemon(True) # 主线程终止,不等待子线程
# t.setDaemon(False)
t.start()
# t.join() # 一直等
# t.join(1) # 等待最大时间
2. 锁
# 1. 只能有一个人使用锁
# lock = threading.Lock() # 只能开一把
# lock = threading.RLock()# 可以开多把
# 2. 多个人同时使用锁
# lock = threading.BoundedSemaphore(3)
# 3. 所有的解脱锁的限制
# lock = threading.Event()
# 4. 肆意妄为
# lock = threading.Condition()
3. 线程池
模式一:直接处理
3. 线程池
模式一:直接处理
def task(url):
"""
任务执行两个操作:下载;保存本地
"""
# response中封装了Http请求响应的所有数据
# - response.url 请求的URL
# - response.status_code 响应状态码
# - response.text 响应内容(字符串格式)
# - response.content 响应内容(字节格式)
# 下载
response = requests.get(url)
# 下载内容保存至本地
f = open('a.log','wb')
f.write(response.content)
f.close()
pool = ThreadPoolExecutor(2)
url_list = [
'http://www.oldboyedu.com',
'http://www.autohome.com.cn',
'http://www.baidu.com',
]
for url in url_list:
print('开始请求',url)
# 去连接池中获取链接
pool.submit(task,url)
模式二:分步处理
模式二:分步处理
def save(future):
"""
只做保存 # future中包含response
"""
response = future.result()
# 下载内容保存至本地
f = open('a.log','wb')
f.write(response.content)
f.close()
def task(url):
"""
只做下载 requests
"""
# response中封装了Http请求响应的所有数据
# - response.url 请求的URL
# - response.status_code 响应状态码
# - response.text 响应内容(字符串格式)
# - response.content 响应内容(字节格式)
# 下载
response = requests.get(url)
return response
pool = ThreadPoolExecutor(2)
url_list = [
'http://www.oldboyedu.com',
'http://www.autohome.com.cn',
'http://www.baidu.com',
]
for url in url_list:
print('开始请求',url)
# 去连接池中获取链接
# future中包含response
future = pool.submit(task,url)
# 下载成功后,自动调用save方法
future.add_done_callback(save)
- 进程
1. 基本使用
from multiprocessing import Process
import time
def task(arg):
time.sleep(arg)
print(arg)
if __name__ == '__main__':
for i in range(10):
p = Process(target=task,args=(i,))
p.daemon = True
# p.daemon = False
p.start()
p.join(1)
print('主进程最后...')
进程锁和线程锁是一样的(使用方式一样)
2. 进程之间的数据共享
特殊的东西
- Array(‘类型’,长度)
- Manager().list() / Manager().dict()
- 报错是因为,主进程走完了,子进程就就会报错
- 写一个input或者什么的,让主进程等,就能防止报错
- 或者jion成为串行。
- 使用进程池,就可以避免
- 第三方工具
from multiprocessing import Process
from threading import Thread
"""
# 验证进程之间数据不共享
def task(num,li):
li.append(num)
print(li) # 数据不是共享的,打印出来就是一个个数字 [1] [2] 。。。
if __name__ == '__main__':
v = []
for i in range(10):
# p = Process(target=task,args=(i,v,))
p = Thread(target=task,args=(i,v,))
p.start()
"""
"""
# 方式一:进程数据共享
from multiprocessing import Process,Array
from threading import Thread
def task(num,li):
li[num] = 1
print(list(li))
if __name__ == '__main__':
v = Array('i',10) # v[0]
for i in range(10):
p = Process(target=task,args=(i,v,))
p.start()
"""
from multiprocessing import Process,Manager
from threading import Thread
def task(num,li):
li.append(num)
print(li)
if __name__ == '__main__':
v = Manager().list()
# v = Manager().dict()
for i in range(10):
p = Process(target=task,args=(i,v,))
p.start()
# p.join()
input('>>>')
3. 进程池
- from concurrent.futures import ProcessPoolExecutor
def call(arg):
data = arg.result()
print(data)
def task(arg):
print(arg)
return arg + 100
if __name__ == '__main__':
pool = ProcessPoolExecutor(5)
for i in range(10):
obj = pool.submit(task,i)
obj.add_done_callback(call)
================== 结论 ==================
IO密集:线程
计算密集:进程
- 协程
pip3 install greenlet
协程永远是一个线程在执行,对线程的一个分片处理。
from greenlet import greenlet
def test1():
print(12)
gr2.switch()
print(34)
gr2.switch()
def test2():
print(56)
gr1.switch()
print(78)
gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch()
二次加工:
自定义:
select实现
现成 :
pip3 install gevent
from gevent import monkey; monkey.patch_all()
import gevent
import requests
def f(url):
response = requests.get(url)
print(response.url,response.status_code)
gevent.joinall([
gevent.spawn(f, 'http://www.oldboyedu.com/'),
gevent.spawn(f, 'http://www.baidu.com/'),
gevent.spawn(f, 'http://github.com/'),
])
- IO多路复用
监听多个socket对象是否有变化(可读,可写,发生错误(异常))
- 示例一:
import socket
import select
# IO多路复用:8002,8001
#
############################### 基于select实现服务端的“伪”并发 ###############################
sk1 = socket.socket()
sk1.bind(('127.0.0.1',8001,))
sk1.listen(5)
sk2 = socket.socket()
sk2.bind(('127.0.0.1',8002,))
sk2.listen(5)
inputs = [sk1,sk2,]
w_inputs = []
while True:
# IO多路复用,同时监听多个socket对象
# - select,内部进行循环操作(1024) 主动查看
# - poll, 内部进行循环操作 主动查看
# - epoll, 被动告知
r,w,e = select.select(inputs,w_inputs,inputs,0.05) # r: readble w: writable e:异常
# r = [sk2,]
# r = [sk1,]
# r = [sk1,sk2]
# r = []
# r = [conn,]
# r = [sk1,Wconn]
#######?
for obj in r:
if obj in [sk1,sk2]:
# 新连接捡来了...
print('新连接来了:',obj)
conn,addr = obj.accept()
inputs.append(conn)
else:
# 有连接用户发送消息来了..
print('有用户发送数据了:',obj)
try:
data = obj.recv(1024)
except Exception as ex:
data = ""
if data:
w_inputs.append(obj)
# obj.sendall(data)
else:
obj.close()
inputs.remove(obj)
w_inputs.remove(obj)
for obj in w:
obj.sendall(b'ok')
w_inputs.remove(obj)
# Socket对象
sk1 = socket.socket()
sk1.bind(('127.0.0.1',8001,))
sk1.listen(5)
while True:
# conn Socket对象,
conn,addr = sk.accept()
conn.recv()
conn.sendall()
- socketserverIO
- IO多路复用
- 线程
import socket
import select
import threading
# IO多路复用:8002,8001
#
############################### 基于select实现服务端的“伪”并发 ###############################
"""
def process_request(conn):
while True:
v = conn.recv(1024)
conn.sendall(b'1111')
sk1 = socket.socket()
sk1.bind(('127.0.0.1',8001,))
sk1.listen(5)
inputs=[sk1,]
while True:
# IO多路复用,同时监听多个socket对象
# - select,内部进行循环操作(1024) 主动查看
# - poll, 内部进行循环操作 主动查看
# - epoll, 被动告知
r,w,e = select.select(inputs,[],inputs,0.05)
for obj in r:
if obj in sk1:
# conn客户端的socket
conn,addr = obj.accept()
t = threading.Thread(target=process_request,args=(conn,))
t.start()
"""
# import socketserver
#
# class MyHandler(socketserver.BaseRequestHandler):
# def handle(self):
# pass
#
#
# server = socketserver.ThreadingTCPServer(('127.0.0.1',8001),MyHandler)
# server.serve_forever()
- 自定义异步非阻塞的框架