Python的多任务 02

易小灯塔
2017-08-07 / 0 评论 / 834 阅读 / 正在检测是否收录...
温馨提示:
本文最后更新于2022年06月22日,已超过888天没有更新,若内容或图片失效,请留言反馈。

线程

概念

程序中一个可以执行的分支

程序默认只有一个执行分支, 就是主线程

想要完成多任务,可以再开辟一个线程,好比再有一个可以执行代码的分支。可以就可以完成多个任务同时在执行。线程是cpu调度的基本单位

 

在python里,使用threading模块来进行程序的多线程操作

t = threading.Thread(target=saySorry)
t.start() #启动线程,即让线程开始执行
使用threading.Thread创建线程


length = len(threading.enumerate()) #查看进程的数量 
# threading.enumerate()是当前程序所有进行中的线程的列表,通过统计其长度,就可以得出当前有多少个线程在进行

threading.current_thread()  #查看当前任务执行的线程
# 放在代码中的任意地方即可知道当前代码执行的是哪个线程,如果是放在函数里,则显示是执行子线程 

将子线程设置成为守护主线程,主线程退出了,那么子线程直接销毁不执行代码了, 以后线程的销毁要依赖主线程

调用setDaemon(True) 子线程名.setDaemon(True) join() 等待子线运行结束后再开启下一个子线程

t1 = threading.Thread(target=sing, args=(5,), kwargs={"name": "insmoin", "age": 18}) 

创建线程,此处sing后面不要带有小括号,只要有函数名即可

1.target是线程执行函数的名字,函数的名字后面不要带有小括号,注意与函数调用的方法区别开来

2.args:执行函数所需要的参数,这个参数要以元组的形式去传,如果只有一个元素,后面不要忘了逗号

3.kwargs:执行函数所需要的参数, 这个参数要以字典方式去传

注意: 调用完start方法以后线程才会放入到线程活动列表中

线程注意点

过使用threading模块能完成多任务的程序开发,为了让每个线程的封装性更完美,所以使用threading模块时,往往会定义一个新的子类class,只要继承threading.Thread就可以了,然后重写run方法

当定义新的子类时,创建好实例对象后,不是通过调用run方法开启线程,而是通过对象.start()开启线程

python的threading.Thread类有一个run方法,用于定义线程的功能函数,可以在自己的线程类中覆盖该方法。而创建自己的线程实例后,通过Thread类的start方法,可以启动该线程,交给python虚拟机进行调度,当该线程获得执行的机会时,就会调用run方法执行线程。

  • 主线程会等待所有的子线程结束后才结束
  • 线程之间执行顺序是无序, 主线程会等待所有的子线程任务执行完成以后程序再退出

多线程共享全局变量的优缺点

  • 优点: 在一个进程内的所有线程共享全局变量,很方便在多个线程间共享数据

  • 缺点: 线程是对全局变量随意遂改可能造成多线程之间对全局变量的混乱(即线程非安全)

    如果多个线程同时对同一个全局变量操作,会出现资源竞争问题,从而数据结果会不正确

     

互斥锁

当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制

某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性

死锁

在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁。

进程

概念

一个程序运行起来后, 代码+用到的资源, 就称为进程, 是操作系统分配资源的基本单位

 

Python进程的使用

导入模块import multiprocessing

获取当前进程编号

导入模块import os

Os.getpid() 获取当前的进程号

在子进程里面获取当前子进程的父进程的编号用的是os.getppid(),注意与os.getpid的区别

multiprocessing.current_process().pid也可以获取当前的进程编号

os.kill() 根据指定的进程的编号杀死指定的进程

查看当前任务由哪个进程执行

multiprocessing.current_process() 

创建进程

实例名 = multiprocessing.Process

 

 

进程之间的通讯Queue

导入模块import multiprocessing

初始化一个Queue对象

q=multiprocessing.Queue(5)
# 括号内的数字代表能够接收的消息数量
# 消息可以是任意的数据类型,字符串,数字,列表,元组,字典,集合等
# 若括号中没有指定最大可接收的消息数量,或数量为负值,那么就代表可接受的消息数量没有上限(直到内存的尽头)

Queue.qsize() # 返回当前队列包含的消息数量
Queue.empty() # 如果队列为空,返回True,反之False
Queue.full()  # 如果队列满了,返回True,反之False

Queue.get([block[, timeout]])
# 获取队列中的一条消息,然后将其从列队中移除,block默认值为True
# 如果block使用默认值,且没有设置timeout(单位秒),消息列队如果为空,此时程序将被阻塞(停在读取状态),直到从消息列队读到消息为止,如果设置了timeout,则会等待timeout秒,若还没读取到任何消息,则抛出"Queue.Empty"异常;
# 如果block值为False,消息列队如果为空,则会立刻抛出"Queue.Empty"异常
Queue.get_nowait() 
# 相当于
Queue.get(False)

Queue.put_nowait(item)
# 相当
Queue.put(item, False) 

 

 

进程池

进程池执行任务的本质: 从进程池中找一个空闲的进程去执行指定的任务

进程池使用同步方式和异步方式执行的区别

进程池有两种执行方式,即同步执行和异步执行,用同步执行的话,主进程会等待进程池里面的任务执行完毕后才开始执行主进程的任务,如果是异步执行的话,则,主进程不会理会进程池里面执行任务的情况,而是直接执行主进程里面的任务

简单来说,同步执行的话,主进程会等待进程池里面的任务执行完毕,而异步执行的话,主进程是不会等待进程池里面执行任务完毕的

 

使用

import multiprocessing  # 导入模块
multiprocessing.Pool()  #创建进程池, 括号内可以放上数字表示该进程池拥有的进程数量 
close()		# 关闭Pool,使其不再接受新的任务
terminate()  # 不管任务是否完成,立即终止
join()		# 主进程阻塞,等待子进程的退出, 必须在close或terminate之后使用
apply(copy_work)  # 进程池使用同步方式执行这个任务,进程池用的进程会按照一定顺序等待其它进程执行完成以后再依次执行 

 

 

迭代器

概念

迭代是访问集合元素的一种方式。

迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。

我们把可以通过for...in...这类语句迭代读取一条数据供我们使用的对象称之为可迭代对象(Iterable)

 

可迭代对象和不可迭代对象

我们把可以通过for...in...这类语句迭代读取一条数据供我们使用的对象称之为可迭代对象(Iterable)

  • 可迭代对象: 列表,元组,字符串,字典,集合,range等
  • 不可迭代对象: 整型,自定义的类等

判断可迭代对象

from collections import Iterable
isinstance([], Iterable)
# 结果为True则是可迭代对象
# isinstance要传入两个参数,第一个是要判断的对象,第二个是Iterable,首字母是大写的i 

判断迭代器

from collections import Iterator
isinstance([], Iterator)
isinstance(iter([]), Iterator)
# 要用iter()函数获取可迭代对象的迭代器,可迭代对象本身不是迭代器来的 

可迭代对象的本质就是可以向我们提供一个 中间人即迭代器帮助我们对其进行迭代遍历使用

生成器

概念

只要在def中有yield关键字的 就称为 生成器

使用了yield关键字的函数不再是函数,而是生成器。(使用了yield的函数就是生成器)

yield有两点作用

  1. 保存当前运行状态(断点),然后暂停执行,即将生成器(函数)挂起

  2. 将yield关键字后面表达式的值作为返回值返回,此时可以理解为起到了return的作用

  3. 可以使用next()函数让生成器从断点处继续执行,即唤醒生成器(函数)

     

唤醒生成器

# 使用next()函数可以使用next()函数让生成器从断点处继续执行,即唤醒生成器(函数)
f = gen()
next(f)
# 使用send()函数的一个好处是可以在唤醒的同时向断点处传入一个附加数据。
f = gen()
f.send('haha')
# 使用__next__()方法
f = gen()
f.__next__() 

 

 

可迭代对象,迭代器,生成器

可迭代对象:

一个具备了iter方法的对象,就是一个可迭代对象

迭代器:

任何具有__next__()方法的对象都是迭代器, 对迭代器调用next()方法可以获取下一个值

迭代器本质上是一个产生值的工厂,每次向迭代器请求下一个值,迭代器都会进行计算出相应的值并返回。

生成器

生成器是一个返回迭代器的函数,其实就是定义一个迭代算法,可以理解为一个特殊的迭代器。调用这个函数就得到一个迭代器,生成器中的yield相当于一个断点,执行到此返回一个值后暂停,从而实现next取值。

生成器是Python中一种非常强大的特性,它让我们能够编写更加简洁的代码,同时也更加节省内存,使用CPU也更加高效

总结:

1.迭代器一定是迭代对象,迭代对象不一定是迭代器

2.生成器一定是迭代器,迭代器不一定是生成器

0

评论 (0)

取消