多任务:
并行:执行方数量 ≥ 被执行任务数量
并发:执行方数量 < 被执行任务数量
并发: 注:(实际上总有一些任务不在执行,因为切换任务的速度相当快,看上去一起执行而已)
线程
1、主线程和子线程的理解:主线程会等待所有的子线程结束后才结束
import threading
import time
def test1():
while True:
print("-----我是线程1-----")
time.sleep(1)
def test2():
while True:
print("-----我是线程2-----")
time.sleep(1)
def main():
# 1.创建线程对象
t1 = threading.Thread(target = test1)
t2 = threading.Thread(target = test2)
# 2. 线程对象开启运行
t1.start()
t2.start()
if __name__ == '__main__':
main()
2、利用enumerate查询当前线程执行数量
3、通过继承threading.Thread()的方式创建线程:使用threading模块时,定义一个新的子类class继承threading.Thread,然后重写run方法
实例:
import threading
import time
class MyThread(threading.Thread):
def run(self):
for i in range(3):
time.sleep(1)
msg = "I'm "+self.name+' @ '+str(i) #name属性中保存的是当前线程的名字
print(msg)
if __name__ == '__main__':
t = MyThread()
t.start()
注:当对象调用start方法时会自动调用MyThread的run方法
4、验证多线程共享全局变量
注:由于多线程之间会产生资源共享,这同时也会带来资源竞争;
子线程线程对全局变量随意遂改可能造成多线程之间全局变量的混乱。
5、互斥锁——解决多线程共享全局变量带来全局变量混乱的问题
1)没有加互斥锁产生的效果
2)加上互斥锁产生的效果
进程(进程代码结构和执行效果与线程差不多)
1 、主进程和子进程的理解
import multiprocessing
import time
def test1():
while True:
print("-----我是进程1-----")
time.sleep(1)
def test2():
while True:
print("-----我是进程2-----")
time.sleep(1)
def main():
# 1.创建进程对象
p1 = multiprocessing.Process(target = test1)
p2 = multiprocessing.Process(target = test2)
# 2. 进程对象开启运行
p1.start()
p2.start()
if __name__ == '__main__':
main()
2、 进程和线程的区别:资源占据:进程>线程,这也就意味着进程所承担的比线程更多东西。
1)功能
进程,能够完成多任务,比如 在一台电脑上能够同时运行多个QQ
线程,能够完成多任务,比如 一个QQ中的多个聊天窗口
2)定义的不同
进程是系统进行资源分配和调度的一个独立单位.
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.
3)区别
一个程序至少有一个进程,一个进程至少有一个线程.
线程的划分尺度小于进程(资源比进程少),使得多线程程序的并发性高。
进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率
线程不能够独立执行,必须依存在进程中
可以将进程理解为工厂中的一条流水线,而其中的线程就是这个流水线上的工人
4)优缺点
线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护;而进程正相反。
3、 利用队列使得进程之间的通信
关于队列函数:特点是先进先出 • Queue.qsize():返回当前队列包含的消息数量;
• Queue.empty():如果队列为空,返回True,反之False ;
• Queue.full():如果队列满了,返回True,反之False; • Queue.get([block[, timeout]]):
获取队列中的一条消息,然后将其从列队中移除,block默认值为True;
1)如果block使用默认值,且没有设置timeout(单位秒),消息列队如果为空,此时程序将被阻塞(停在读取状态),直到从消息列队读到消息为止,如果设置了timeout,则会等待timeout秒,若还没读取到任何消息,则抛出"Queue.Empty"异常;
2)如果block值为False,消息列队如果为空,则会立刻抛出"Queue.Empty"异常;
• Queue.get_nowait():相当Queue.get(False); • Queue.put(item,[block[,
timeout]]): 将item消息写入队列,block默认值为True;
1)如果block使用默认值,且没有设置timeout(单位秒),消息列队如果已经没有空间可写入,此时程序将被阻塞(停在写入状态),直到从消息列队腾出空间为止,如果设置了timeout,则会等待timeout秒,若还没空间,则抛出"Queue.Full"异常;
2)如果block值为False,消息列队如果没有空间可写入,则会立刻抛出"Queue.Full"异常;
• Queue.put_nowait(item):相当Queue.put(item, False);
实例:在父进程中创建两个子进程,一个往Queue里写数据,一个从Queue里读数据:
import multiprocessing
import time
# 获取数据
def write_data(q):
# 假设data为获取到的数据
get_data = [0,1,2,3]
# 向队列中写数据
for temp in get_data:
q.put(temp)
print(get_data)
print("\n------数据下载完成,等待数据处理-----\n")
# 处理数据
def read_data(q):
#建一个空列表用于将获取到的数据存储起来用以处理
deal_data_list = list()
while True:
deal_data = q.get()
deal_data_list.append(deal_data)
if q.empty():
break
print(deal_data_list)
print("\n------数据处理完成-----\n")
def main():
# 创建一个队列
q = multiprocessing.Queue(4)
# 将队列当做实参传递给子进程
p1 = multiprocessing.Process(target = write_data,args = (q,))
p2 = multiprocessing.Process(target = read_data,args = (q,))
p1.start()
p1.join()
p2.start()
p2.join()
if __name__ == '__main__':
main()
输出:
4、 进程池
进程池用于单执行的进程比较多时,利用进程池使得同时调用一定量的进程,而不是一拥而上导致CPU没办法有效的执行;
初始化Pool时,可以指定一个最大进程数,当有新的请求提交到Pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到指定的最大值,那么该请求就会等待,直到池中有进程结束,才会用之前的进程来执行新的任务
from multiprocessing import Pool
import os, time, random
def worker(msg):
t_start = time.time()
print("%s开始执行,进程号为%d" % (msg,os.getpid()))
# random.random()随机生成0~1之间的浮点数
time.sleep(random.random()*2)
t_stop = time.time()
print(msg,"执行完毕,耗时%0.2f" % (t_stop-t_start))
po = Pool(3) # 定义一个进程池,最大进程数3
for i in range(0,10):
# Pool().apply_async(要调用的目标,(传递给目标的参数元祖,))
# 每次循环将会用空闲出来的子进程去调用目标
po.apply_async(worker,(i,))
print("----start----")
po.close() # 关闭进程池,关闭后po不再接收新的请求
po.join() # 等待po中所有子进程执行完成,必须放在close语句之后
print("-----end-----")
协程
1、迭代器
1) 判断一个对象是否可以被迭代:使用 isinstance() 判断一个对象是否是可迭代对象(Iterable):
2) 判断一个对象是否为迭代器:可以使用 isinstance() 判断一个对象是否是迭代器对象(Iterator):
3) 自定义一个对象分析可迭代对象的本质
class Classmate(object):
def __init__(self):
self.names =list()
def add(self,name):
self.names.append(name)
def __iter__(self):
"""一个对象想要成为一个可迭代对象的前提是拥有iter方法"""
#__iter__方法的返回值是一个迭代器,self作为参数意味着将本函数的引用传递给返回函数
return ClassIterator(self)
class ClassIterator(object):
"""自定义一个可迭代对象的迭代器"""
def __init__(self,obj):
self.obj =obj
self.current_num = 0
def __iter__(self):
pass
# 每执行一次for对象调用一次next方法,next返回值为for得到的值
def __next__(self):
if self.current_num < len(self.obj.names):
ret = self.obj.names[self.current_num]
self.current_num+=1
return ret
else:
raise StopIteration #返回一个让程序结束的异常
classmate = Classmate()
classmate.add("哈登")
classmate.add("杜兰特")
classmate.add("詹姆斯")
for name in classmate:
print(name)
4)优化自定义迭代器
class Classmate(object):
def __init__(self):
self.names =list()
self.current_num = 0
def add(self,name):
self.names.append(name)
def __iter__(self):
"""一个对象想要成为一个可迭代对象的前提是拥有iter方法"""
#__iter__方法的返回值是一个迭代器,self作为参数意味着将本函数的引用传递给返回函数
return self
def __next__(self):
"""每执行一次for对象调用一次next方法,next返回值为for得到的值"""
if self.current_num < len(self.names):
ret = self.names[self.current_num]
self.current_num+=1
return ret
else:
raise StopIteration #返回一个让程序结束的异常
classmate = Classmate()
classmate.add("哈登")
classmate.add("杜兰特")
classmate.add("詹姆斯")
for name in classmate:
print(name)
5) 迭代器的优点:占据空间小,不用直接生成一个列表
6) 迭代器的应用:生成费波纳茨数列前10个数
class FibIterator(object):
"""斐波那契数列迭代器"""
def __init__(self, n):
"""
:param n: int, 指明生成数列的前n个数
"""
self.n = n
# current用来保存当前生成到数列中的第几个数了
self.current = 0
# num1用来保存前前一个数,初始值为数列中的第一个数0
self.num1 = 0
# num2用来保存前一个数,初始值为数列中的第二个数1
self.num2 = 1
def __next__(self):
"""被next()函数调用来获取下一个数"""
if self.current < self.n:
num = self.num1
self.num1, self.num2 = self.num2, self.num1+self.num2
self.current += 1
return num
else:
raise StopIteration
def __iter__(self):
"""迭代器的__iter__返回自身即可"""
return self
if __name__ == '__main__':
fib = FibIterator(10)
for num in fib:
print(num, end=" ")
print()
2、生成器:特殊的迭代器
1)创建生成器的方法
方法1:把一个列表生成式的 [ ] 改成 ( )
方法2:使用yield对象
def create_num(all_num):
a, b = 0, 1
current_num = 0
while current_num < all_num:
yield a
a, b =b ,a+b
current_num +=1
obj = create_num(10)
for num in obj:
print(num, end=" ")
print()
探究生成器的执行顺序(利用生成器是一种特殊的迭代器可以调用next函数)
def create_num(all_num):
print("----1----")
a, b = 0, 1
current_num = 0
while current_num < all_num:
print("----2----")
yield a
print("----3----")
a, b =b ,a+b
current_num +=1
print("----4----")
obj = create_num(10)
ret = next(obj)
print(ret)
ret = next(obj)
print(ret)
ret = next(obj)
print(ret)
2)除了可以使用next()函数来唤醒生成器继续执行外,还可以使用send()函数来唤醒执行
好处是可以在唤醒生成器的时候给生成器传入一个值;
**
第一次不要用send()唤醒生成器,要么第一次不传参
**
def create_num(all_num):
a, b = 0, 1
current_num = 0
while current_num < all_num:
ret = yield a
print("ret的值:",ret)
a, b =b ,a+b
current_num +=1
obj = create_num(10)
ret = next(obj)
print(ret)
ret = obj.send("我是ret")
print(ret)
(3)利用yield实现协程(并发)
import time
def work1():
while True:
print("----work1---")
yield
time.sleep(0.5)
def work2():
while True:
print("----work2---")
yield
time.sleep(0.5)
def main():
w1 = work1()
w2 = work2()
while True:
next(w1)
next(w2)
if __name__ == "__main__":
main()
(4)使用greenlet模块实现协程,使得切换任务变的更加简单
先安装greenlet模块:sudo pip3 install greenlet
from greenlet import greenlet
import time
def test1():
while True:
print("----work1---")
gr2.switch()
time.sleep(0.5)
def test2():
while True:
print("----work2---")
gr1.switch()
time.sleep(0.5)
gr1 = greenlet(test1)
gr2 = greenlet(test2)
#切换到gr1中运行
gr1.switch()
(5)使用gevent模块实现协程:遇到延时(gevent.sleep())切换任务执行
先安装gevent模块:sudo pip3 install gevent
from gevent import monkey
import gevent
import random
import time
# 有耗时操作时需要
monkey.patch_all() # 将程序中用到的耗时/堵塞操作的代码,换为gevent中自己实现的模块
def coroutine_work(coroutine_name):
for i in range(3):
print(coroutine_name, i)
time.sleep(random.random())
gevent.joinall([
gevent.spawn(coroutine_work, "work1"),
gevent.spawn(coroutine_work, "work2")
])