一.线程
定义:python的thread模块是比较底层的模块,python的threading模块是对thread做了一些包装的,可以更加方便的被使用
threading模块:线程是CPU内核执行的最小单位,通过threading模块可以创建线程。一般不用。
demo01 单线程执行
import threading
def music():
"""听音乐"""
print("music")
def main():
"""创建线程"""
#创建线程并开启
threading.Thread(target=music).start()
if __name__ == '__main__':
main()
demo01 多线程执行
import threading
import time
def music():
"""听音乐"""
while True:
print("music")
time.sleep(1)
def dance():
"""跳舞"""
while True:
print("跳舞")
time.sleep(1)
def main():
"""创建线程"""
#创建线程并开启
threading.Thread(target=music).start()
threading.Thread(target=dance()).start()
if __name__ == '__main__':
main()
二.进程
定义:一个程序运行起来后,代码+用到的资源称之为进程,它是操作系统分配资源的基本单元。
进程的创建:multiprocessing 模块就是跨平台版本的多进程模块,提供了一个Process类来代表一个进程对象,这个对象可以理解为一个独立的进程,可以执行另外的事情
demo01 进程多任务
#线程是CPU执行的最小单位 ,进程是用来分配CPU资源
#进程的创建
import multiprocessing
import time
import threading
def dance():
while True:
print("跳舞")
time.sleep(1)
def song():
while True:
print("唱歌")
time.sleep(1)
def main():
"""主函数,用于创建多进程"""
print("主进程",multiprocessing.current_process().name)#打印进程信息
print("主线程",threading.current_thread().name) #打印线程信息
multiprocessing.Process(target=dance).start()
multiprocessing.Process(target=song).start()
if __name__ == '__main__':
main()
demo02 使用进程池
# 一个读一个写
import multiprocessing
# 定义一个队列
import time
# 工作中不建议使用进程池,进程池还有bug
# 后期如果开多线程,跟协程还有bug
# 进程的队列
# queue = multiprocessing.Queue(3)
# 进程池的队列,这个吉多专门给进程池创建api,功能上使用上都跟线程的队列是一样
queue = multiprocessing.Manager().Queue(3)
def read():
while True:
print("读")
print(queue.get())
time.sleep(1)
# pool.terminate() # 结束
def write():
while True:
queue.put("123")
print("写")
time.sleep(1)
def main():
# 创建一个进程池
pool = multiprocessing.Pool(2)
# pool.apply(write) # 这种方法是同步方法,进程池中的进程只有一个一个执行
# # pool.terminate()
pool.apply_async(write) # 这个就是不同步
pool.apply_async(read) # 这个直接运行会有bug
# time.sleep(2)
# # 直接关
# pool.terminate() # 关闭
#
# # 如果使用了apply_async这个方法必须使用join
pool.close() # 这个让我们的进程池不在添加其他的进程了
pool.join() # 让我们的主进程等我们一下
if __name__ == '__main__':
main()
demo03 进程的使用
import multiprocessing
import os
import time
num = 0
# 如何确定一个数据是唯一的,进程号+进程内的地址编号这两个确定唯一的数据
def read():
for temp in range(10):
input() # 进程内不能使用input(),这个只能用在主进程内
print("read", num)
print(id(num))
time.sleep(1)
print("子进程", os.getpid())
print("子进程", os.getppid()) # 那个进程创建了你
def main():
# input()
print("主进程:", id(num), os.getpid())
"""进程"""
multiprocessing.Process(target=read).start()
if __name__ == '__main__':
main()
三.协程
定义:协程又称微线程 ,是python中另外一种实现多任务的方式,占用比线程更小的执行单位。
迭代器:for … in … 是常见的迭代器模型。迭代器是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象/只能往前不能退后。我们可以通过next()方法,获取下一数据。这里L就是迭代器 L = [ x2 for x in range(5)]
生成器:为了达到记录当前状态,并撇和next() 函数进行迭代使用,我们可以采用更简便的语法,即生成器(generator)生成器是一类特殊的迭代器。 这里G就是生成器 G = ( x2 for x in range(5))
2.简单实现协程yield
demo01 简单实现协程
import time
def work1():
while True:
print("--------work1----------")
yield
time.sleep(0.5)
def work2():
while True:
print("--------work2----------")
yield
time.sleep(0.5)
if __name__ == '__main__':
w1 = work1()
w2 = work2()
while True:
next(w1)
next(w2)
3.协程 - greenlet
greenlet模块对协程进行了封装,从而使得切换任务变得简单
使用greenlet,必须使用 .switch() 进行任务切换
安装greenlet模块: $ sudo pip3 install greenlet
demo02
#greenlet 协程
from greenlet import greenlet
import time
def dance():
"""dance"""
while True:
print("------dance--------")
time.sleep(0.5)
gr2.switch()
def music():
"""music"""
while True:
print("------music--------")
time.sleep(0.5)
gr1.switch()
gr1 = greenlet(dance)
gr2 = greenlet(music)
def main():
"""greenlet协程"""
gr1.switch()
if __name__ == '__main__':
main()
4.协程 - gevent
gevent模块:由于IO操作非常耗时,经常使程序处于等待状态,gevent 为我们自动切换协程,保证了总有greenlet在执行,而不是等待IO.
gevent模块安装:pip3 install gevent
demo01 gevent协程的使用
import gevent #协成的库 ,只有在python中才有多任务
import time
def dance():
"""跳舞"""
while True:
print("跳舞")
gevent.sleep(1)
def song():
"""唱歌"""
while True:
print("唱歌")
gevent.sleep(1)
def main():
"""主函数"""
spawn_dance = gevent.spawn(dance)
spawn_song = gevent.spawn(song)
#加入join
spawn_dance.join()
spawn_song.join()
if __name__ == '__main__':
main()
demo02 协程的耗时使用补丁
import gevent
import time
#请求Monkey来打补丁
from gevent import monkey
#把所有耗时的api在执行的时候自动换成gevent中的耗时api
#使用协成必须使用monkey打补丁
monkey.patch_all()
def dance():
"""跳舞"""
while True:
print("跳舞")
gevent.sleep(1)
# from greenlet import greenlet
def song():
"""唱歌"""
while True:
print("唱歌")
gevent.sleep(1)
def main():
"""主函数,协成版唱歌跳舞"""
#把任务放到协成中
spawn_dance = gevent.spawn(dance)
spawn_song = gevent.spawn(song)
#加入join
spawn_dance.join()
spawn_song.join()
if __name__ == '__main__':
main()
5.案例:并发下载器
demo01 并发下载原理
from gevent import monkey
import gevent import urllib.request # 有耗时操作时需要
monkey.patch_all()
def my_downLoad(url):
print('GET: %s' % url)
resp = urllib.request.urlopen(url)
data = resp.read()
print('%d bytes received from %s.' % (len(data), url))
gevent.joinall([
gevent.spawn(my_downLoad, 'http://www.baidu.com/'),
gevent.spawn(my_downLoad, 'http://www.itcast.cn/'),
gevent.spawn(my_downLoad, 'http://www.itheima.com/'),
])
四. http协议的请求和相应
请求: 构成(请求头,空行 ,请求体)
请求头 :请求方式 请求地址 http协议 (GET/index.html HTTP/1.1)
换行符 :\r\n
GET请求的格式 :
GET /path HTTP/1.1
Header1: Value1
Header2: Value2
...
每个Header一行一个 ,换行符 是 \r\n
POST请求的格式:
POST /path HTTP/1.1
Header1: Value1
Header2: Value2
body date goes here...
当遇到连续两个\r\n时 ,Header部分结束,后面的数据全部是body
响应 : 构成(响应头 空行 响应体)
HTTP/1.1 200 OK\r\n
\r\n
body