**
一、多任务
**
1、多任务就是同一时刻多个任务同时执行。
例如开车看路的同时操作方向盘,对于电脑来说就是运行多个程序,例如浏览器,QQ,音乐盒同时运行。
2、电脑实现多任务的原理
例如三个程序同时运行是因为CPU在多个应用程序之间高速切换的结果,CPU在多个程序之间快速往返执行,我们肉眼根本看不到卡顿,导致我们的错觉感觉是同时运行的结果,如果电脑运行的时候会出现卡顿,就是因为CPU切换不过来了。
3、单核,双核CPU介绍:
单核CPU指的是CPU中有一个核心(形象理解CPU是人的头,核心是头里面包含的大脑),用来处理程序。
双核/四核CPU就是CPU中有2个或者4个核心,(一个脑袋中长了2个或者4个大脑)相当于有2个单核CPU或者是4个单核CPU。
4、在python中实现多任务有三种方式,进程、线程、协程
**
二、进程
**
1、什么是进程
电脑上运行应用程序时,双击就会打开,当我们双击时,操作系统将程序装载到内存中,操作系统为它分配资源,然后才能运行。运行起来的应用程序就称之为进程,也就是说当程序不运行的时候我们叫他程序,运行的时候就就是进程;程序和进程的对应关系是:程序只有一个,但是进程可以多个,例如QQ可以打开多个界面。
2、创建多进程
1、不使用多进程实现控制台,打印唱歌然后再打印跳舞
import time#调用 time模块
def sing():
for i in range(3):
print('------正在唱歌--------')
time.sleep(1)
def dance():
for i in range(3):
print('-------跳舞----------')
time.sleep(1)
if __name__=='__main__':
sing()
dance()
2、使用进程让唱歌和跳舞一起执行。
import time,multiprocessing
def sing():
for i in range(3):
print('------唱首歌-------')
time.sleep(1)
def dance():
for i in range(3):
print('-------正在跳舞-------')
time.sleep(1)
def main():
p1=multiprocessing.Process(target=sing)
p2=multiprocessing.Process(target=dance)
p1.start()
p2.start()
if __name__ == '__main__':
main()#唱歌跳舞同时执行,提高了运行效率
3、进程的状态
在程序运行的过程中,由于被操作系统的调度算法控制,程序会进入几个状态:就绪,运行和阻塞
4、进程之间通讯
进程可以理解为复制了一份程序有加载到了内存了,进程之间是独立的,如果我想两个进程之间进行通讯怎么办呢?我们可以使用Queue 队列,队列是一种先进先出的存储数据结构,两个进程通讯,就是一个子进程往queue中写内容,另一个进程从queue中取出数据。就实现了进程间的通讯了。
.queue队列
1)q=multiprocessing.Queue(3)#3表示只能存放3个数据
2)put()方法,向队列中存放数据。如果队列已满,此方法将
组塞,知道队列中有项目可用为止。
3)get()返回q中的一个项目,如果q为空,此方法将阻塞,直到队列中有项目可用为止
4)get_nowait():不等待,直接抛出异常
5)full()如果q已满,返回为True
6)q.empty()如果调用此方法时q为空,返回True
7)q.qsize()查看队列中数据个数
下面将上述方法演练一下:
import multiprocessing#调用模块
q=multiprocessing.Queue(3)#创建一个队列对象
q.put('hello')#存入数据
q.put(123)
q.put([1,2,3])
# q.put('aaaa')#存多了,堵塞
print("有多少数据:",q.qsize())#查看队列中数据个数
print(q.full())#判断队列数据是否存满
print(q.get())#取出一个数据
print(q.get())
print(q.get())
#print(q.get())#多一个取出也会堵塞
print(q.empty())#判断队列数据是否空了
print(q.get_nowait())#不等待,抛出异常
练习1使用queue模拟多任务下载和处理数据
**注意:**传参的时候args=()括号里面的一定要放元组类型
import multiprocessing,time#调用模块
def download_data(q):
data=['aa','bb','cc']
for i in data:
q.put(i)#将从列表里面下载的数据保存
print('下载完了')
def processing_data(q):
for i in range(q.qsize()):
print(q.get())#将保存的数据取出来
print('处理完了')
def main():
q=multiprocessing.Queue()
q1=multiprocessing.Process(target=download_data,args=(q,))#子进程1,传参元组类型
q2=multiprocessing.Process(target=processing_data,args=(q,))#子进程2
q1.start()#开启进程1
time.sleep(1)#让进程2晚1秒开启
q2.start()#开启进程2
if __name__=='__main__':
main()#主进程
**
二、线程
**
线程概念
由于进程是资源拥有者,创建、撤消与切换存在较大的内存开销,因此需要引入轻型进程即线程,进程是资源分配的最小单位,线程是CPU调度的最小单位(程序真正执行的时候调用的是线程),每一个进程中至少有一个线程。
知识点:
1、Python3 线程中常用的模块为:threading模块
2、线程可以传递参数。
3、.join()方法:
join()方法功能:当前线程执行完后其他线程才会继续执行。
4、.setDaemon()方法:
setDaemon()将当前线程设置成守护线程来守护主线程:当主线程结束后,守护线程也就结束,不管是否执行完成。
应用场景:qq多个聊天窗口,就是守护线程
注意:需要在子线程开启的时候设置成守护线程,否则无效
5、实例方法:
线程对象的一些实例方法
- getName(): 获取线程的名称。
- setName(): 设置线程的名称。
- isAlive(): 判断当前线程存活状态。
下面我们用threading模块创建线程,并且加入上述的各种方法
import time,threading
def sing(num):#传参num
for i in range(num):
print('唱歌')
time.sleep(1)
def dance(num):#传参num
for i in range(num):
print('跳舞')
time.sleep(1)
def main():
p1=threading.Thread(target=sing,args=(3,))#args传参
p2=threading.Thread(target=dance,args=(3,))
# p1.setDaemon(True)#守护线程p1
# p2.setDaemon(True)#守护线程p2
print(p1.is_alive())#判断p1线程存活状态。
p1.start()
# p1.join()#加在这里在p2之前加载,p1执行完后,p2和主线程才执行
print(p2.is_alive())#判断p2线程存活状态。
p2.start()
#p1.join()#p1,p2执行完后主线程才执行
# p1.setName('张飞')#设置线程的名称
# p2.setName('关羽')#设置线程的名称
# p1_name = p1.getName()#获取线程的名称
# p2_name = p2.getName()#获取线程的名称
# print(p1_name)#打印获取线程的名称
# print(p2_name)#打印获取线程的名称
if __name__=='__main__':
main()
print('少林功夫加唱歌跳舞有没有搞头')
#子线程和主线程谁先抢到CPU谁执行
6、使用继承方法开启线程
1)定义一个继承hreading.Thread类
2)复写父类的run()方法
import threading#调用模块
class MyThread(threading.Thread):#定义继承一个类
def __init__(self,num):
super().__init__()#调用父类__init__,继承传参
self.num=num
def run(self):
for i in range(self.num):
print('唱歌',i)
if __name__=='__main__':
my_thread=MyThread(3)#创建对象,正常方式传参
my_thread.start()#开启子线程
7、线程之间共享全局变量
import threading
g_num=100#设定一个全局变量
def test1():
global g_num#修改全局变量
g_num+=1
print('test1-----',g_num)
def test2():
print('test2------',g_num)#打印全局变量
def main():
p1=threading.Thread(target=test1)
p2=threading.Thread(target=test2)
p1.start()
p2.start()
if __name__=='__main__':
main()
print('main...',g_num)
8、共享全局变量存在的问题
多线程开发的时候共享全局变量会带来资源竞争效果,也就是数据不安全。
例如看下方代码:
import threading
g_num=0
def test1(num):
global g_num
for i in range(num):
g_num+=1
print('test1-----',g_num)
def test2(num):
global g_num
for i in range(num):
g_num+=1
print('test......',g_num)
def main():
p1=threading.Thread(target=test1,args=(10000000,))
p2=threading.Thread(target=test2,args=(10000000,))
p1.start()
p2.start()
if __name__=='__main__':
main()
上面代码运行时产生的效果是:
test… 11510621
test1----- 12112380
为什么会这样呢?直白一点说就是多个线程同时操作全局变量,争夺CPU造成数据不全,数据产生覆盖现象,从而造成数据不准确。为了线程同步能够保证多个线程访问竞争资源(全局内容),最简单的同步机制就是使用互斥锁。互斥锁保证了每一次只有一个线程进入写入操作,从而保证了多线程下数据的安全性。
import threading
g_num=0
def test1(num):
global g_num
lock.acquire()#加锁
for i in range(num):
g_num+=1
lock.release()#解锁
print('test1-----',g_num)
def test2(num):
global g_num
lock.acquire()#加锁
for i in range(num):
g_num+=1
lock.release()#解锁
print('test......',g_num)
lock=threading.Lock()#我们要创建一个全局锁的对象
def main():
p1=threading.Thread(target=test1,args=(10000000,))
p2=threading.Thread(target=test2,args=(10000000,))
p1.start()
p2.start()
if __name__=='__main__':
main()