2018-08-04(15.5)多线程

python基础语法(15.5)

多线程

初步了解多线程

(个人理解)电脑的cpu一般情况下一次只能给一个进程提供服务,它给每个进程提供服务的时间是相同的,到达时间后就会给下一个进程提供服务,这种行为被称之为时间片轮转。但是由于cpu的速度实在太快了,所以造成看似好像是所有程序一起并发执行。

但是有些时候,有些程序较为耗费资源,一般cpu轮转提供给它的服务不足以支撑它运行。这时候他就会开多天线程,cpu会轮转给每个线程提供单位时间的服务。如此一来就可以让该程序获得更多的资源。

线程是应用程序中工作的最小单位。Python当前的多线程库没有实现优先级,线程组,线程也不能被停止,暂停,恢复,中断。

多线程具有以下有点:

  • 使用线程可以把程序中长时间占用资源的任务放到后台处理。
  • 可以让显示界面更加灵活,通过点击等事件去触发一些线程,并且可以在线程进行的过程中给用户相应的反馈。
  • 提升程序运行速度。
  • 一些不需要连续使用系统资源的任务,系统可以在任务间歇将CPU分配给其它进程,更好的利用系统资源。

如何使用多线程

Python的标准库给我们提供了两个标准模块:_thread 和threading。

_thread是低级模块,threading是高级模块,它对 _thread 进行了封装,我们常用threading模块。

threading提供的类

Thread

该类可以按一下两种方式安全的进行子类化:

  1. 通过将可调用对象传递给构造函数。
  2. 通过重写子类中的run()方法。

这是一个构造函数,调用它需要传入关键字参数,其中

​ *group默认为空,为ThreadGroup将来扩展保留的。

​ *target是run()调用的可调用的对象方法。默认为none,表示不调用任何内容。

​ *name是线程名称,默认情况下,构造函数的唯一名称,形式为“Thread-N”,其中N是小十进制数。

​ *args是目标调用的参数元组,默认为()

​ *kwargs是目标关键字参数的字典调用,默认为{}

​ 如果子类重写构造函数,则必须确保调用积累构造函数(Thread_init_())

Lock

Rlock

Condition

Event

Timer

local

[Bounded]Semaphore

实例方法

  • isAlive():返回线程是否在运行,在启动后,终止前都称之为运行。

  • get/setName(name):获取/设置线程名

  • start()“线程准备就绪,等待CPU调度

  • join([timeout]):阻塞当前的上下文环境的线程,直到调用此方法的线程终止或达到指定的timeout(可选参数)

    • 上下文

    每个线程都有属于它的一组CPU寄存器,称之为线程的上下文,上下文反应了线程上次运行该线程的CPU寄存器状态。

    指令指针和堆栈指针寄存器是线程上下文中两个重要的寄存器,线程总是在进程得到的上下文中运行的,这些地址都用于标注拥有线程的进程地址空间中的内存。

threading模块提供的常量

在threading.TIMEOUT_MAX设置threading全局超时时间。

以函数的形式来开启线程

import threading 
import time
#将要执行的方法作为参数传递给Thread的构造方法
def action(arg):
    time.sleep(1)
    print(threading.current_thread()) # 返回当前的线程对象
    
    print("The arg is:{0}".format(arg))#注意是format不是formate

for i in range(4):
    t = threading.Thread(target = action,args = (i,)) # 创建调用action的线程
    t.start() # 就绪线程,会自动执行run()来运行线程
    
if __name__ == "__main__":
    print(threading.current_thread())
    print("main thread End")

运行结果:

<_MainThread(MainThread, started 24928)>
main thread End

The arg is :0


The arg is :2
The arg is :1

The arg is :3

用类来包装线程对象

即自己定义一个类来继承Thread,通过重写run()方法来开启多线程。

代码实例:

class MyThread(threading.Thread):
    def __init__(self,arg):
        super(MyThread,self).__init__() #调用父类
        self.arg = arg #初始化传入参数
    def run(self): #重写该方法,定义每个线程要运行的函数
        time.sleep(1)
        print("The arg is :{0}".format(self.arg))
for i in range(4):
    t = MyThread(i)
    t.start()

运行结果:

The arg is :0
The arg is :2
The arg is :1
The arg is :3

threading模块提供的常用方法:

  • threading.currentThread():返回当前的线程变量。
  • threading.enumerate():返回一个包含正在运行的线程的list。(正在运行的线程是指线程启动后,结束前,不包括启动前和终止后的线程。)
  • threading.active Count():返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。

后台线程与前台线程

is/setDeamon(bool):获取/设置是否为后台线程(默认为前台线程(False))。

注: 在start()之前设置

区别:

  1. 后台线程,主线程结束,后台线程立刻停止。
  2. 前台线程,主线程结束,等待前台线程执行完程序停止。

相同点:

​ 不论是前台还是后台,在主线程运行时它们也运行。

代码实例:

if __name__ == '__main__':
    for i in range(4):
        t = MyThread(i)
        t.setDaemon(True)
        t.start()

    print('main thread end!')

运行结果:

main thread end!

如果设置为前台线程,运行结果为:

main thread end!
The arg is :0
The arg is :1
The arg is :3
The arg is :2

join() 线程阻塞

该方法会阻塞当前上下文环境线程,直到调用此方法的线程终止或者到达指定的timeout。即使设置了setDeamon(True)主线程依旧要等待子线程结束。

** 线程必须先start()然后再join()**

代码实例:

if __name__ == '__main__':
    th=[]
    for i in range(100):
        t = MyThread(i)
        th.append(t)
        t.start()
        ```
        不能直接写成t.join(),这样只会阻塞主线程一次,无法确保再阻塞过程中线程都执行完。
        下面的for循环时要阻塞主线程,创建的线程数那么多次,可以保证主线程最后执行完。
        这种不同于再上面的for循环里写join(),写在上面会使每个线程不光阻塞主线程,也阻塞接下来的线程。时多线程无意义。
        ```
    for tt in th:
        tt.join()
    #设置join之后,主线程等待子线程全部执行完成后或者子线程超时后,主线程才结束
    print('main thread end!')

你可能感兴趣的:(2018-08-04(15.5)多线程)