多线程(一)

什么是线程? ➢线程是一个进程的实体,一个进程可以拥有多个线程,一个线程必须有一个父进程。线程是由表示程序运行状态的寄存器(如程序计数器、栈指针)以及堆栈组成,它是比进程更小的单位。 ➢线程是程序中的一个执行流。一个执行流是由CPU运行程序代码并操作程序的数据所形成的。因此,线程被认为是以CPU为主体的行为。 ➢线程不包含进程地址空间中的代码和数据,线程是计算过程在某一时刻的状态。所以,系统在产生一个线程或各个线程之间切换时,负担要比进程小得多。 ➢线程是一个用户级的实体,线程结构驻留在用户空间中,能够被普通的用户级函数直接访问。 ➢一个线程本身不是程序,它必须运行于一个程序(进程)之中。因此,线程可以定义为一个程序中的单个执行流。 •多线程是指一个程序中包含多个执行流,多线程是实现并发的一种有效手段。一个进程在其执行过程中,可以产生多个线程,形成多个执行流。每个执行流即每个线程也有它自身的产生、存在和消亡的过程。 •多线程程序设计的含义就是可以将程序任务分成几个并行的子任务。 线程和进程的区别 ➢进程是资源分配的最小单位,线程是程序执行的最小单位 ➢ 进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。而线程是共享进程中的数据的,使 用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程 的开销也比进程要小很多。 ➢ 线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要以通信的方式(IPC)进行。不过如何处理好同步与互斥是编写多线程程序的难 点。 ➢ 但是多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间。 线程的状态图 线程阻塞通常是指一个线程在执行过程中暂停,以等待某个条件的触发; Python线程threading模块 Threading模块提供的类: Thread,Lock,Rlock,Condition,Semaphore,Event,Timer,local等。 threading模块提供的常用方法: ✓ threading.currentThread(): 返回当前的线程变量。 ✓ threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线 程启动后、结束前,不包括启动前和终止后的线程。 ✓ threading.activeCount():返回正在运行的线程数量,与len(threading.enumerate())有相同的结果 。 threading模块提供了更方便的API来操作线程。 Thread是threading模块中最重要的类之一,可以使用它来创建线程。 该类创建线程有有两种方式: 1.直接创建threading.Thread类的对象,初始化时将可调用对象作为参数传入。 2.通过继承Thread类,重写它的run方法。 Thread类的构造方法: __init__(self, group=None, target=None, name=None, args=(),kwargs=None, verbose=None) 参数说明: •group:线程组,目前还没有实现,库引用中提示必须是None; •target:要执行的方法; •name:线程名; •args/kwargs:要传入方法的参数。 Thread类的方法 1)isAlive():返回线程是否在运行。正在运行指的是启动后,终止前。 2)getName(name):获取线程名。 3)setName(name):设置线程名。 4)isDaemon(bool):判断线程是否随主线程一起结束。 5)setDaemon(bool):设置是否为守护线程。初始值从创建该线程的线程继承而来,默认为False,当没有非守护线程仍在运行时,程序将终止。比如,主线程A中,创建了子线程B,并且在主线程A中调用了B.setDaemon(),意思是,把线程B设置为守护线程,这时候,要是主线程A执行结束了,就不管子线程B是否完成,一并和主线程A退出。这就是setDaemon方法的含义,这基本和join是相反的。此外,还有个要特别注意,必须在start() 方法调用之前调用此方法,如果不设置为守护线程,程序有可能会被无限挂起。 6)start():启动线程。 7)join([timeout]):阻塞当前上下文环境的线程,直到调用此方法的线程终止或到达指定的等待时间timeout(可选参数)。即当前的线程要等调用join()这个方法的线程执行完,或者是达到规定的时间。比如,主线程A中,创建了子线程B,并且在主线程A中调用了B.join(),那么,主线程A会在调用的地方等待,直到子线程B完成操作后,才可以接着往下执行,那么在调用这个线程时可以使用被调用线程的join方法。 8)run():用于表示线程活动的方法,通常需要重写,编写代码实现做需要的功能。 创建threading.Thread线程 #encoding=utf-8 from threading import Thread import time def run(a = None,b = None): print(a,b) time.sleep(1) #创建一个子线程 t = Thread(target=run, args=("this is a","thread")) #获取线程的名字 print(t.getName()) #判断线程是否还活着,在start后,在执行完毕前调用isAlive()才会返回true print(t.isAlive()) t.start() print(t.isAlive()) t.join() print(t.isAlive()) 通过继承Thread类创建线程 使用Threading模块创建线程,直接从threading.Thread继承,然后重写__init__方法和run方法 #encoding=utf-8 from threading import Thread import time class MyThread(Thread): #需要调用父类的__init__方法 def __init__(self,a): super(MyThread,self).__init__() #Thread.__init__(self) self.a = a #需重写父类的run方法 def run(self): time.sleep(self.a) print("sleep:",self.a) t1 = MyThread(2) t2 = MyThread(3) t1.start() t2.start() t1.join() t2.join() 注意:多线程执行本身是没有先后顺序的 run方法是在start()时候自动调用的; 继承Thread类-新增run方法参数 注意: 继承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() thread2.start() time.sleep(10) thread1.stop() thread2.stop() if __name__ == "__main__": test() 线程退出 下面的程序实现了多线程执行后自动退出,无需设定结束的时间也没有使用join函数,主线程会自己退出。 #encoding=utf-8 import threading import time #如果flag设定为1线程启动后会直接退出 exitFlag = 0 class MyThread(threading.Thread): def __init__(self,threadID,name,counter): threading.Thread.__init__(self) self.threadID = threadID self.name = name self.counter = counter def run(self): print("Starting " + self.name) print_time(self.name,5,self.counter) print("Exiting " + self.name) def print_time(threadName,delay,counter): while counter:#利用counter变量控制线程是否结束 if exitFlag: thread.exit() time.sleep(delay) print("%s:%s" %(threadName,time.ctime(time.time()))) counter -= 1 thread1 = MyThread(1,"线程1",1) thread2 = MyThread(1,"线程2",2) thread1.start() thread2.start() print("主进程结束!") Join函数用法 #encoding=utf-8 import threading import time def context(tJoin): print("in threadContext") tJoin.start() tJoin.join() 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() 主程序中tJoin = threading.Thread(target=join)这句代码执行后,只是创建了一个线程对象 tJoin,但并未启动线程。 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.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(999) t1.setDaemon(True)#设置线程为守护线程 t1.start() print("I am the father thread.") 由于线程启动后,然后主线程就结束了,而子线程的run方法需要等待5秒,这时候子线程已经随着主线程的退出而退出了;所以不会打印run里面的语句; 从结果可以看出,调用了setDaemon()函数后,子线程被设置为守护线程,主线程打印内容后就结束了,不管子线程是否执行完毕,一并被终止了。可见join()函数与setDaemon()函数功能是相反的。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章