目录
线程的概念
多线程
线程和进程的区别
线程的状态图
Python线程模块
Python线程threading模块
threading模块提供的类:
threading模块提供的常用方法:
Thread类
该类创建线程有有两种方式:
Thread类拥有的方法
创建threading.Thread线程
通过继承Thread 类创建线程
继承Thread类-新增run方法参数
线程退出
Join()函数典型用法
守护线程
是指一个程序中包含多个执行流,多线程是实现并发的一种有效手段。一个进程在其执行过程中,可以产生多个线程,形成多个执行流。每个执行流即每个线程也有它自身的产生、存在和消亡的过程;
多线程程序设计的含义就是可以将程序任务分成几个并行的子任务;
⚫ _thread(低版本使用,不推荐)
⚫ threading
⚫ Multiprocessing
Thread,Lock,Rlock,Condition,Semaphore,Event,Timer,local等。
Thread是threading模块中最重要的类之一,可以使用它来创建线程。
1.直接创建threading.Thread类的对象,初始化时将可调用对象作为参数传入。
2.通过继承Thread类,重写它的run方法。
1.构造方法
__init__(self, group=None, target=None, name=None, args=(), kwargs=None, *, daemon=None)
参数说明:
group:应当为None,为将来实现Python Thread类的扩展而保留。
target:是被 run()方法调用的回调对象;默认应为None, 意味着没有对象被调用。
name:为线程名字。默认形式为’Thread-N’的唯一的名字被创建,其中N 是比较小的十进制数。
args:是目标调用参数的tuple,默认为空元组()。
kwarg:是目标调用的参数的关键字dictionary,默认为{}。
daemon:设置守护进程,默认为None
2.isAlive():
返回线程是否在运行。正在运行指的是启动后,终止前
3.getName():
获取线程名
4.setName(name):
设置线程名;这名字是只用来进行标识目的的字符串,没有其他作用。多个线程可以取同一名字。最初的名字通过构造函数设置
5.isDaemon(线程对象):
判断线程是否随主线程一起结束;返回线程的守护线程标志,返回布尔值
6.setDaemon(daemonic):
设置守护线程标志为布尔值daemonic。它必须在start()调用之前被调用。初始值继承至创建线程。当没有活动的非守护线程时,整个Python程序退出。
设置是否为守护线程。初始值从创建该线程的线程继承而来,默认为False,当没有非守护线程仍在运行时,程序将终止。比如,主线程A中,创建了子线程B,并且在主线程A中调用了B.setDaemon(),意思是,把线程B设置为守护线程,这时候,要是主线程A执行结束了,就不管子线程B是否完成,一并和主线程A退出。这就是setDaemon方法的含义,这基本和join是相反的。此外,还有个要特别注意,必须在start() 方法调用之前调用此方法,如果不设置为守护线程,程序有可能会被无限挂起。
7.start():
启动线程活动
8.join([timeout]):
阻塞当前上下文环境的线程,直到调用此方法的线程终止或到达指定的等待时间timeout(可选参数)。即当前的线程要等调用join()这个方法的线程执行完,或者是达到规定的时间。比如,主线程A中,创建了子线程B,并且在主线程A中调用了B.join(),那么,主线程A会在调用的地方等待,直到子线程B完成操作后,才可以接着往下执行,那么在调用这个线程时可以使用被调用线程的join方法。
9.run():
用于表示线程活动的方法,通常需要重写,编写代码实现做需要的功能。
#encoding=utf-8
from threading import Thread
import time
#自定义run函数
def run(a = None, b = None) :
print(a, b )
time.sleep(1)
t = Thread(target = run, args = ("this is a", "thread")) #此时线程是新建状态
print(t.getName()) #获得线程对象名称
print(t.isAlive()) #判断线程是否活着,在start后,在执行完毕前调用isAlive()才会返回True
t.start() #启动线程
print(t.isAlive())
t.join() #主线程等待子线程t执行结束
print(t.isAlive())
输出结果:
使用Threading模块创建线程,直接从threading.Thread继承,然后重写__init__方法和run方法。
#encoding=utf-8
from threading import Thread
import time
class MyThread(Thread) :
def __init__(self, a) :
"调用父类的构造方法"
super(MyThread, self).__init__()
self.a = a
def run(self) :
time.sleep(self.a)
print("sleep :", self.a)
t1 = MyThread(2)
t2 = MyThread(4)
t1.start()
t2.start()
t1.join()
t2.join()
***多线程的执行是没有顺序的,本例主要是增加了较长的等待时间,才看起来有执行顺序。
注意:
继承Thread类的新类MyThread构造函数中必须要调用父类的构造方法,这样才能产生父类的构造函数中的参数,才能产生线程所需要的参数。新的类中如果需要别的参数,直接在其构造方法中添加即可。同时,新类中,在重写父类的run方法时,它默认是不带参数的,如果需要给他提供参数,需要在类的构造函数中指定,因为在线程执行过程中,run方法是线程自己去调用的,不用我们手动调用,所以没法直接给传递参数,只能在构造方法中设定好参数,然后在run方法中再去取这些参数。
代码示例:
#encoding=utf-8
import threading
import time
class timer(threading.Thread):
def __init__(self, num, interval):
threading.Thread.__init__(self)
self.thread_num = num
self.interval = interval
self.thread_stop = False
def run(self):
while not self.thread_stop:
print('Thread Object(%d), Time:%s\n' %(self.thread_num, time.ctime()) )
time.sleep(self.interval)
def stop(self):
self.thread_stop = True
def test():
thread1 = timer(1, 1)
thread2 = timer(2, 2)
thread1.start() #执行类里面的run方法
thread2.start()
time.sleep(10)
thread1.stop()
thread2.stop() #停止线程
if __name__ == '__main__':
test()
下面的程序实现了多线程执行后自动退出,无需设定结束的时间也没有使用join函数,主线程会自己退出。
#encoding=utf-8
import threading
import time
import sys
class myThread (threading.Thread):
def __init__(self, threadID, name, counter):
"继承父类threading.Thread"
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.counter = counter
def run(self):
"把要执行的代码写到run函数里面线程在创建后会直接运行run函数"
print("Starting " + self.name)
print_time(self.name,5, self.counter)
print("Exiting " + self.name )
exitFlag = 0 #是否每个线程要进行工作后再退出,如果设定1则所有线程启动后直接退出
def print_time(threadName, delay, counter):
while counter:
if exitFlag:
return
time.sleep(delay)
print("%s: %s" % (threadName, time.ctime(time.time())))
counter -= 1
# 创建新线程
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)
# 开启线程
thread1.start()
thread2.start()
print("Exiting Main Thread")
运行结果:
示例代码:
在一个线程中,启动另外一个线程,执行后退出
# encoding: UTF-8
import threading
import time
def context(tJoin):
print('in threadContext.')
tJoin.start()
# 将阻塞tContext直到threadJoin终止。
tJoin.join()
# tJoin终止后继续执行。
print('out threadContext.')
def join():
print('in threadJoin.')
time.sleep(1)
print('out threadJoin.')
tJoin = threading.Thread(target = join)
tContext = threading.Thread(target = context, args = (tJoin,))
tContext.start()
运行结果:
思路解析:
1.主程序中tJoin = threading.Thread(target=join)这句代码执行后,只是创建了一个线程对象tJoin,但并未启动线程;
2.tContext = threading.Thread(target=context, args=(tJoin,));tContext.start() 这两句执行后,创建了一个线程对象tContext并启动该线程(打印in threadContext.),同时将tJoin线程对象作为参数传给context函数,在context函数中,启动了tJoin这个线程,同时该线程又调用了join()函数(tJoin.join()),那tContext线程将等待tJoin这线程执行完成后,才能继续tContext线程后面的,所以先执行join()函数,打印输出:in threadJoin. out threadJoin.
3.tJoin线程执行结束后,继续执行tContext线程,打印输出了out threadContext.,于是就看到我们上面看到的输出结果,并且无论执行多少次,结果都是这个顺序。但如果将context()函数中tJoin.join()这句注释掉,再执行该程序,打印输出的结果顺序就不定了,因为此时这两线程就是并发执行的。
代码示例:
# encoding: UTF-8
import threading
import time
class MyThread(threading.Thread):
def __init__(self, id):
threading.Thread.__init__(self)
def run(self):
time.sleep(5)
print("This is " + self.getName())
if __name__ == "__main__":
t1 = MyThread(10)
t1.setDaemon(True) # 将子线程设置为守护线程
t1.start()
#t1.join()
print("I am the father thread.")
输出结果:
# encoding: UTF-8
import threading
import time
class MyThread(threading.Thread):
def __init__(self, id):
threading.Thread.__init__(self)
def run(self):
time.sleep(5)
print("This is " + self.getName())
if __name__ == "__main__":
t1 = MyThread(10)
#t1.setDaemon(True) #注释掉线程守护
t1.start()
t1.join()
print("I am the father thread.")
输出结果:
从结果可以看出,调用了setDaemon()函数后,子线程被设置为守护线程,主线程打印内容后就结束了,不管子线程是否执行完毕,一并被终止了。可见join()函数与setDaemon()函数功能是相反的。