线程
之前了解了操作系统的发展史,也知道了进程和线程的概念,归纳一下就是:
进程:本质上就是一段程序的运行过程(抽象的概念)
线程:最小的执行单元,是进程的实体
线程的调用
在python中,一般通过导入threading模块来调用线程。 threading 模块建立在thread 模块之上。thread模块以低级、原始的方式来处理和控制线程,而threading 模块通过对thread进行二次封装,提供了更方便的api来处理线程,先看一段直接调用的代码:
直接调用:
# -*- coding: utf-8 -*- import threading,time def func1(n): print('传给我的是%s'%n) time.sleep(3) def func2(): print('我没有参数的') time.sleep(5) if __name__ == '__main__': t1 = threading.Thread(target=func1,args=(5,)) #target传入的是函数名,不带括号,args要以元组形式传入参数 t2 = threading.Thread(target=func2) #没有参数就不用传 t1.start() #运行线程,本质是调用run方法 t2.start() print('ending......') >>>传给我的是5 >>>我没有参数的 >>>ending......
运行的时候几乎同时打印了三条结果,然后等待约5s再停止,如果是单线程的话就要8s的时间才能运行完,这样做提升了效率。其次这个程序一共有多少线程呢?答案是3个,除了t1和t2两个实例化得到的子线程,还有程序本身的主线程,还记得那句话吗?一个程序至少有一个进程,一个进程至少有一个线程。
继承方式调用线程:
import time import threading class Mythread(threading.Thread): def __init__(self,n): threading.Thread.__init__(self) self.n = n def func1(self): print('传给我的是%s'%self.n) time.sleep(self.n) def run(self): ##定义每个线程要运行的函数 self.func1() if __name__ == '__main__': t1 = Mythread(2) t2 = Mythread(5) t1.start() t2.start() >>>传给我的是2 >>>传给我的是5
也是直接打印两条结果,然后等待5s程序结束。一般这种方式很少用到,都是直接调用比较快。如果不穿参数的话,__init__方法可以不用写,但是run函数一定要写的,至于为什么,你可以通过找继承的父类去推,这里就不啰嗦了。
线程的join方法
join是线程实例化后也具有的方法,像上边的start一样,那么join有什么作用呢?
import threading from time import ctime,sleep def music(name): print ("Begin listening to %s. %s" %(name,ctime())) sleep(2) print("end listening %s"%ctime()) def moive(title): print ("Begin recording the %s! %s" %(title,ctime())) sleep(5) print('end recording %s'%ctime()) threads = [] t1 = threading.Thread(target=ListenMusic,args=('毛不易的歌',)) t2 = threading.Thread(target=RecordBlog,args=('白蛇',)) threads.append(t1) threads.append(t2) if __name__ == '__main__': for t in threads: t.start() #t.join()#串行 # t.join() # t1.join() # t2.join()########分别试一下这几种join位置下的结果 print('endtime is %s'%ctime())
我们逐个分析,不加join时:
Begin listening to 毛不易的歌. Thu Apr 11 17:47:01 2019 Begin recording the 白蛇! Thu Apr 11 17:47:01 2019 endtime is Thu Apr 11 17:47:01 2019 end listening Thu Apr 11 17:47:04 2019 end recording Thu Apr 11 17:47:06 2019
加上一个t1.join时:
Begin listening to 毛不易的歌. Thu Apr 11 17:49:42 2019 Begin recording the 白蛇! Thu Apr 11 17:49:42 2019 end listening Thu Apr 11 17:49:45 2019 endtime is Thu Apr 11 17:49:45 2019 end recording Thu Apr 11 17:49:47 2019
注意endtime的变化,然后去试试其他情况就可以知道(注意for循环里面和外面的t的区别),join作用就是在子线程完成运行之前,这个子线程的父线程将一直被阻塞。
线程的setDaemon方法
通过join我们可以阻塞线程,那么setDaemon就刚好相反,就是将线程声明为守护线程,但是必须在start() 方法调用之前设置, 如果不设置为守护线程程序会被无限挂起。当我们 在程序运行中,执行一个主线程,如果主线程又创建一个子线程,主线程和子线程 就分兵两路,分别运行,那么当主线程完成想退出时,会检验子线程是否完成。如 果子线程未完成,则主线程会等待子线程完成后再退出。但是有时候我们需要的是 只要主线程完成了,不管子线程是否完成,都要和主线程一起退出,这时就可以 用setDaemon方法啦
import threading from time import ctime,sleep def music(name): print ("Begin listening to %s. %s" %(name,ctime())) sleep(2) print("end listening %s"%ctime()) def moive(title): print ("Begin recording the %s! %s" %(title,ctime())) sleep(5) print('end recording %s'%ctime()) threads = [] t1 = threading.Thread(target=ListenMusic,args=('毛不易的歌',)) t2 = threading.Thread(target=RecordBlog,args=('白蛇',)) threads.append(t1) threads.append(t2) if __name__ == '__main__': #t1.setDaemon(True) t2.setDaemon(True) for t in threads: #t.setDaemon(True) #一定要在start前 t.start() #t.join()#串行 # t.join() t1.join() # t2.join()########分别试一下这几种join位置下的结果 print('endtime is %s'%ctime())
看一下结果,白蛇没看完就停了
Begin listening to 毛不易的歌. Thu Apr 11 18:03:42 2019 Begin recording the 白蛇! Thu Apr 11 18:03:42 2019 end listening Thu Apr 11 18:03:45 2019 endtime is Thu Apr 11 18:03:45 2019
其他方法
# run(): 线程被cpu调度后自动执行线程对象的run方法 # start():启动线程活动。 # isAlive(): 返回线程是否活动的。 # getName(): 返回线程名。 # setName(): 设置线程名。 threading模块提供的一些方法: # threading.currentThread(): 返回当前的线程变量。 # threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。 # threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
基本的线程就是这样使用的,后面讲的就是同步和异步的概念,还有各种锁,慢慢来。