进程和线程的比较
1. 进程的开销比线程的开销大很多
2. 进程之间的数据是隔离的,但是,线程之间的数据不隔离
3. 多个进程之间的线程数据不共享----->还是让进程通信(IPC)------->进程下的线程也通信了---->队列
GIL全局解释器锁
Python在设计之初就考虑到要在主循环中,同时只有一个线程在执行。虽然 Python 解释器中可以“运行”多个线程,但在任意时刻只有一个线程在解释器中运行。
"""对Python解释器的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同一时刻只有一个线
程在运行"""
### 背景信息
1. Python代码运行在解释器上嘛,有解释器来执行或者解释
2. Python解释器的种类:
1、CPython 2、IPython 3、PyPy 4、Jython 5、IronPython
3. 当前市场使用的最多(95%)的解释器就是CPython解释器
4. GIL全局解释器锁是存在于CPython中
5. 结论是同一时刻只有一个线程在执行? 想避免的问题是,出现多个线程抢夺资源的情况
比如:现在起一个线程,来回收垃圾数据,回收a=1这个变量,另外一个线程也要使用这个变量a,当垃圾回收线程还没没有把变量a回收完毕,另一个线程就来抢夺这个变量a使用。
怎么避免的这个问题,那就是在Python这门语言设计之处,就直接在解释器上添加了一把锁,这把锁就是为了让统一时刻只有一个线程在执行,言外之意就是哪个线程想执行,就必须先拿到这把锁(GIL), 只有等到这个线程把GIL锁释放掉,别的线程才能拿到,然后具备了执行权限.
"""得出结论:GIL锁就是保证在统一时刻只有一个线程执行,所有的线程必须拿到GIL锁才有执行权限"
互斥锁
"""在多线程的情况下,同时执行一个数据,会发生数据错乱的问题"""
n = 10
from threading import Lock
import time
def task(lock):
lock.acquire()
global n
temp = n
time.sleep(0.5)
n = temp - 1
lock.release()
"""拿时间换空间,空间换时间 时间复杂度"""
from threading import Thread
if __name__ == '__main__':
tt = []
lock=Lock()
for i in range(10):
t = Thread(target=task, args=(lock, ))
t.start()
tt.append(t)
for j in tt:
j.join()
print("主", n)
线程队列(线程里使用队列)
为什么线程中还有使用队列?
"""
同一个进程下多个线程数据是共享的
为什么先同一个进程下还会去使用队列呢
因为队列是
管道 + 锁
所以用队列还是为了保证数据的安全
"""
线程队列:
1. 先进先出
2. 后进先出
3. 优先级的队列
from multiprocessing import Queue
"""线程队列"""
import queue
queue.Queue()
# queue.Queue 的缺点是它的实现涉及到多个锁和条件变量,因此可能会影响性能和内存效率。
import queue
q=queue.Queue() # 无限大、
q.put('first')
q.put('second')
q.put('third')
q.put('third')
q.put('third')
q.put('third')
q.put('third')
print(q.get())
print(q.get())
print(q.get())
## 后进先出
import queue
# Lifo:last in first out
q=queue.LifoQueue()
q.put('first')
q.put('second')
q.put('third')
print(q.get())
print(q.get())
print(q.get())
## 优先级队列
import queue
q=queue.PriorityQueue()
#put进入一个元组,元组的第一个元素是优先级(通常是数字,也可以是非数字之间的比较),数字越小优先级越高
q.put((20,'a'))
q.put((10,'b'))
q.put((30,'c'))
print(q.get())
print(q.get())
print(q.get())
'''
结果(数字越小优先级越高,优先级高的优先出队):
(10, 'b')
(20, 'a')
(30, 'c')
'''
进程池和线程池的使用
池:池子、容器类型,可以盛放多个元素
进程池:提前定义好一个池子,然后,往这个池子里面添加进程,以后,只需要往这个进程池里面丢任务就行了,然后,有这个进程池里面的任意一个进程来执行任务
线程池:提前定义好一个池子,然后,往这个池子里面添加线程,以后,只需要往这个线程池里面丢任务就行了,然后,有这个线程池里面的任意一个线程来执行任务
def task(n, m):
return n+m
def task1():
return {'username':'kevin', 'password':123}
"""开进程池"""
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
def callback(res):
print(res) # Future at 0x1ed5a5e5610 state=finished returned int>
print(res.result()) # 3
def callback1(res):
print(res) # Future at 0x1ed5a5e5610 state=finished returned int>
print(res.result()) # {'username': 'kevin', 'password': 123}
print(res.result().get('username'))
if __name__ == '__main__':
pool=ProcessPoolExecutor(3) # 定义一个进程池,里面有3个进程
## 2. 往池子里面丢任务
pool.submit(task, m=1, n=2).add_done_callback(callback)
pool.submit(task1).add_done_callback(callback1)
pool.shutdown() # join + close
print(123)
多线程爬取网页
import requests
def get_page(url):
res=requests.get(url)
name=url.rsplit('/')[-1]+'.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(2)
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)
协程理论
进程
资源分配
线程
执行的最小单位
协程
"""他是程序员自己意淫出来的,它不是在操作系统中实际存在的"""
# 协程就是单线程下的并发
并发:
切换+保存状态
# 以前的并发的切换其实是进程或者线程在切换
既然是单线程下了,你说节省资源不
协程是最节省资源的,进程是最消耗资源的,其次是线程
协程实现高并发
服务端:
from gevent import monkey;
monkey.patch_all()
import gevent
from socket import socket
# from multiprocessing import Process
from threading import Thread
def talk(conn):
while True:
try:
data = conn.recv(1024)
if len(data) == 0: break
print(data)
conn.send(data.upper())
except Exception as e:
print(e)
conn.close()
def server(ip, port):
server = socket()
server.bind((ip, port))
server.listen(5)
while True:
conn, addr = server.accept()
# t=Process(target=talk,args=(conn,))
# t=Thread(target=talk,args=(conn,))
# t.start()
gevent.spawn(talk, conn)
if __name__ == '__main__':
g1 = gevent.spawn(server, '127.0.0.1', 8080)
g1.join()
客户端:
import socket
from threading import current_thread, Thread
def socket_client():
cli = socket.socket()
cli.connect(('127.0.0.1', 8080))
while True:
ss = '%s say hello' % current_thread().getName()
cli.send(ss.encode('utf-8'))
data = cli.recv(1024)
print(data)
for i in range(5000):
t = Thread(target=socket_client)
t.start()