多任务
①、什么是多任务
多任务就是同时运行多个应用程序。例如QQ,微信等等同时运行。
②、电脑实现多任务的原理
CPU在多个应用程序间快速切换运行,让人感觉不到停顿。
③、python中实现多任务的3中方式
进程,线程,协程
一、进程
1、什么是进程
运行起来的程序,就是进程。不运行的时候,就是程序。程序只有一个,进程可以有多个。
2、创建多进程----需要导入multiprocessing多进程模块
from multiprocessing import Process
import time
def sing():
for i in range(3):
print("---正在唱歌---",i)
time.sleep(1)
def dance():
for i in range(3):
print("---正在跳舞---",i)
time.sleep(1)
def main():
p1 = Process(target = sing) #创建进程p1,执行sing函数
p2 = Process(target = dance) #创建进程p2,执行dance函数
p1.start() #开始执行进程p1
p2.start() #开始执行进程p2
if __name__ == '__main__': #开启2个子进程p1,p2
main()
3、多进程之间相互独立
from multiprocessing import Process
a = 1
def func1():
global a
a += 1
print("func1:",a)
def func2():
global a
a += 1
print("func2:",a)
def main():
p1 = Process(target = func1)
p2 = Process(target = func2)
p1.start()
p2.start()
if __name__ == '__main__':
main()
4、进程的状态
程序运行过程中,由于被操作系统的调度算法控制,程序会进入几个状态:就绪,运行,阻塞。
①、就绪(Ready)状态
当进程已分配到除CPU以外所有的必要资源,只要分配到CPU便可立即执行,这时的进程状态称之为就绪状态。
②、执行/运行(Running)状态
当进程已分配到CPU,其程序正在运行,此时的进程状态称之为执行/运行状态。
③、阻塞(Blocked)状态
正在执行的进程,由于某个事件发生而无法执行时,便放弃CPU时而停止时,此时的进程状态称之为阻塞状态。
例如等待I/O完成,申请缓冲区不能满足,等到信号等等。
from multiprocessing import Process
def func1():
print("func1")
def func2():
print("func2")
def main():
p1 = Process(target = func1)
x = input("请输入:") #程序运行到这,等待输入,进入阻塞状态
p2 = Process(target = func2)
p1.start()
p2.start()
if __name__ == '__main__':
main()
5、进程之间通讯----需要创建Queue队列类
两个进程通讯,就是一个子进程往Queue队列中写入数据,另一个子进程从Queue中取出数据。这就实现了进程间的通讯了。
①、创建Queue(maxsize)队列对象,maxsize表示队列中允许的最大项数。如果无此参数,则无数量大小限制。
②、args=(),给函数传递参数
from multiprocessing import Process
def func1(a,b):
print("func1:",a + b)
def func2(name):
print("func2:",name)
def main():
p1 = Process(target = func1,args = (1,2))
p2 = Process(target = func2,args = ("张三",))
p1.start()
p2.start()
if __name__ == '__main__':
main()
③、put(),向队列中存放数据
from multiprocessing import Queue
q = Queue(2) #创建队列,队列最大项为2
q.put("消息1")
print(q) #<multiprocessing.queues.Queue object at 0x0000000001D87470>,返回一个队列对象
q.put("消息2")
print(q) #<multiprocessing.queues.Queue object at 0x0000000001D87470>,返回一个队列对象
q.put("消息3") #超出队列最大项,无法再输入
print(q) #超出队列最大项,无法再输入
④、qsize(),获取队列中数据的数量
from multiprocessing import Queue
q = Queue(2)
print(q.qsize()) #0,还未放入数据,数量为0
q.put("消息1")
print(q.qsize()) #1
q.put("消息2")
print(q.qsize()) #2
⑤、full(),判断队列中是否存放满了
from multiprocessing import Queue
q = Queue(2)
print(q.full()) #False,还未写入数据,未满
q.put("消息1")
print(q.full()) #False,写入一条数据,未满
q.put("消息2")
print(q.full()) #True,已经写入两条数据,已经写满
⑥、get(),获取数据
from multiprocessing import Queue
q = Queue(2)
q.put("消息1")
q.put("消息2")
print(q.get()) #消息1
print(q.get()) #消息2
from multiprocessing import Process,Queue
q = Queue(3)
q.put("消息1")
q.put("消息2")
q.put("消息3")
while q.qsize() > 0:
print(q.get())
⑦、empty(),判断队列中的数据是否空了,True表示空了
from multiprocessing import Queue
q = Queue(2)
q.put("消息1")
q.put("消息2")
print(q.get()) #消息1
print(q.empty()) #False
print(q.get()) #消息2
print(q.empty()) #True
from multiprocessing import Process,Queue
q = Queue(3)
q.put("消息1")
q.put("消息2")
q.put("消息3")
try:
q.put("消息4",timeout=3,block=False)
except:
print("消息队列满了")
print(q.get())
print(q.get())
print(q.get())
try:
print(q.get(timeout=2,block=True))
except Exception as f:
print(f)
print("消息队列空了")
print("结束")
"""
消息队列满了
消息1
消息2
消息3
消息队列空了
结束
"""
练习:用多线程实现,两个人生产包子,一个人吃包子
from multiprocessing import Process,Queue
import time
def chef_1(q,name):
for i in range(1,3):
a = "第{}个{}包子".format(i,name)
print("生产:",a)
q.put(a)
time.sleep(1)
def chef_2(q,name):
for i in range(1,3):
a = "第{}个{}包子".format(i,name)
print("生产:",a)
q.put(a)
time.sleep(1)
def eat(q):
while True:
try:
a = q.get(timeout = 2)
print("吃:",a)
except:
break
def main():
q = Queue(3)
p1 = Process(target = chef_1,args = (q,"张三"))
p2 = Process(target = chef_2,args = (q,"李四"))
p3 = Process(target = eat,args = (q,))
p1.start()
p2.start()
p3.start()
if __name__ == '__main__':
main()
6、进程池----使用Pool类创建进程池
①、join()主程序等待所有子进程执行完毕,必须在close()之后
②、close()等待所有进程结束才关闭进程池
from multiprocessing import Process,Queue,Pool
import time
def func(num):
for i in range(1,11):
print(num,"进程开始",i)
print(num,"进程结束",i)
time.sleep(2)
def main():
p = Pool(4)
for i in range(1, 11):
p.apply_async(func, (i,)) #应用异步
p.close()
p.join()
if __name__ == '__main__':
main()
迅雷下载器
from multiprocessing import Pool
import time
def down_load(movie_name):
for i in range(5):
print("电影:{},下载进度:{}%".format(movie_name,i / 4 * 100))
time.sleep(1)
return movie_name
def alert(movie_name):
print("恭喜{}下载完成了...".format(movie_name))
def main():
movie_lst = ["西红柿首富", "功夫熊猫", "叶问", "功夫", "战狼2", "红海行动"]
pool = Pool(2)
for movie_name in movie_lst:
pool.apply_async(down_load, (movie_name,), callback=alert)
pool.close()
pool.join()
if __name__ == '__main__':
main()
二、线程
1、什么是线程
进程是资源拥有者,创建,撤销与切换存在较大的内存开销,因此需要引入轻量型进程,即线程。
2、进程和线程之间的关系
进程是资源分配的最小单位,线程是CPU调度的最小单位,程序真正执行的时候调用的是线程。每个进程中至少有一个线程。
3、创建线程----需要导入threading线程处理模块
from threading import Thread
import time
count = 0
def sing():
for i in range(3):
global count
count += 1
print(count, "唱歌")
time.sleep(1)
def dance():
for i in range(3):
global count
count += 1
print(count, "跳舞")
time.sleep(1)
def main():
t1 = Thread(target=sing)
t2 = Thread(target=dance)
t1.start()
t2.start()
if __name__ == '__main__':
main()
①、args=(),给函数传递参数
from threading import Thread
import time
def sing(num):
for i in range(num):
print(i,"---正在唱歌---")
time.sleep(1)
def dance(num):
for i in range(num):
print(i,"---正在跳舞---")
time.sleep(1)
def main():
t1 = Thread(target = sing,args = (3,))
t2 = Thread(target = dance,args = (3,))
t1.start()
t2.start()
if __name__ == '__main__':
main()
②、join(),当前线程执行完后,其他线程才会继续执行
from threading import Thread
import time
def sing(num):
for i in range(num):
print(i,"---正在唱歌---")
time.sleep(1)
def dance(num):
for i in range(num):
print(i,"---正在跳舞---")
time.sleep(1)
def main():
t1 = Thread(target = sing,args = (3,))
t2 = Thread(target = dance,args = (3,))
t1.start()
t1.join() #在t2前加载,t1执行完后,t2和主线程才执行
t2.start()
if __name__ == '__main__':
main()
③、steDaemon()将当前线程设置成守护线程来守护主程序,当主程序结束后,守护线程就结束,不管是否执行完成。
注意:需要在子线程开启的时候设置守护线程,否则无效。
④、线程对象提供的方法
getName(),获取线程的名称
setName(),设置线程的名称
isAlive(),判断当前线程存活状态
⑤、threading模块提供的方法
currentThread(),返回当前的线程变量。
enumerate(),返回一个包含正在运行的线程的列表。正在运行指的是线程启动后,结束前,不包括启动前和终止后的线程。
activeCount(),返回正在运行的线程数量。与len(threading.enumerate())有相同效果。
4、使用继承方式开启线程
首先定义一个类,继承threading.Thread类
然后重载父类的run()方法。
from threading import Thread
import time
class MyThread(Thread):
def __init__(self,num):
super().__init__()
self.num = num
def run(self):
for i in range(self.num):
print("---run---",i)
time.sleep(1)
if __name__ == '__main__':
my_thread = MyThread(3)
my_thread.start()
5、线程之间共享全局变量
from threading import Thread
g_num = 2
def func1():
global g_num
g_num += 3
print("func1",g_num)
def func2():
global g_num
g_num += 5
print("func2",g_num)
def main():
t1 = Thread(target=func1)
t2 = Thread(target=func2)
t1.start()
t2.start()
print("main", g_num)
if __name__ == '__main__':
main()
6、共享全局变量的问题
多线程开发的时候共享全局变量,会带来资源竞争效果。也就是数据不安全。
from threading import Thread
g_num = 0
def func1(num):
global g_num
for i in range(num):
g_num += 1
print("func1 =",g_num)
def func2(num):
global g_num
for i in range(num):
g_num += 5
print("func2 =",g_num)
def main():
t1 = Thread(target=func1, args=(1000000,))
t2 = Thread(target=func2, args=(1000000,))
t1.start()
t2.start()
print("main =", g_num)
if __name__ == '__main__':
main()
练习:
from multiprocessing import Process,Queue
from threading import Thread
def a(q):
t_aa = Thread(target = aa, args = (q,))
t_aa.start()
t_aa.join()
def aa(q):
q.put([1, 2, 3, ])
def b(q):
t_bb = Thread(target = bb, args = (q,))
t_bb.start()
t_bb.join()
def bb(q):
print(q.get())
def main():
q = Queue()
p_a = Process(target = a,args = (q,))
p_b = Process(target = b,args = (q,))
p_a.start()
p_b.start()
if __name__ == '__main__':
main()