获取内容资料
Python开发

python多线程教程,马哥教育python教程

import threadingimport timeimport datetime# 该类是自定义的多线程类# 多己写多线程时仿造记类实现自己的多线程类即可class MyThread(threading.Thread):def __init__(self):threading.Thread.__init__(self)# 必须实现函数,run函数被start函数调用def run(self):thread_name = threading.current_thread.nameprint(f”开始线程: {thread_name}”)self.print_timeprint(f”退出线程: {thread_name}”)# 可选函数,此处函数的代码可移到run函数内部,也可放到MyThread之外,无关紧要# 线程要做的具体事务函数,我们这里就打印两轮时间def print_time(self):count = 2thread_name = threading.current_thread.namewhile count:time.sleep(1)print(f”{thread_name}: {datetime.datetime.now.strftime(‘%Y-%m-%d %H:%M:%S %f’)}”)count -= 1# 该类是一个演于调用MyThread的类# 其实其代码也完全可以放在if __name__ == “__main__”处class TestClass:def __init__(self):passdef main_logic(self):# 创建新线程实例thread_1 = MyThreadthread_2 = MyThread# 启动新线程thread_1.startthread_2.start# thread_1.join即当前线程(亦即主线程)把时间让给thread_1,待thread_1运行完再回到当前线程# thread_2.join即当前线程(亦即主线程)把时间让给thread_2,待thread_1运行完再回到当前线程# join方法非阻塞# 如果没对某个线程使用join方法,那么当前线程(亦即主线程)不会等待该线程执行完再结束,他会直接结束# 在多线程的进程中,主线程的地位和其他线程的地位是平等的,不会说主线程退出了就会导致整个进程,进而导致其他线程被迫终止# 自己把这两句join注释掉再运行一遍,可以更好理解这里的说法thread_1.jointhread_2.joinprint(“退出主线程”)if __name__ == “__main__”:obj = TestClassobj.main_logicView Code运行结果如下:

2.2 多线程间的同步上一小结的代码运行可以成功,但在输出的时候,可以看到是很混乱的:线程1刚打印完自己的名字还没打印时间线程2就抢着打印自己的名字了。

造成这种结果的原因是,默认的多线程中,各线程之间是没有感知的。比如线程2并不知道线程1执行到了哪里(反过来亦然),是刚打印完名字还是已经打印完名字和时间还是其他什么状态,都是不知道的。

所谓同步最主要的就是使用某种方法让线程之间能在一定程度上知道对方在做什么,至少的目标是在使用共用资源时能告诉对方我正在使用你先不要用,实现这“至少”目标最常用的方法是使用锁。

Python3中锁对应的类是threading.Lock,可通过该类的acquire方法来获取锁,然后通过该类的release方法来释放锁。

import sysimport threadingimport timeimport datetime# 该类是自定义的多线程类# 多己写多线程时仿造记类实现自己的多线程类即可class MyThread(threading.Thread):def __init__(self,threading_lock):threading.Thread.__init__(self)# 同步添加处2/4:承接传进来的锁self.threading_lock = threading_lock# 注意锁必须定义在线程类外部,不能如下在线程类内部自己定义锁# 因为如果锁在线程类内部才定义,每个线程都是不同的线程类实例,那么各线程间的锁变量本质上就不是同一个锁变量了# self.threading_lock = threading.Lock# 必须实现函数,run函数被start函数调用def run(self):thread_name = threading.current_thread.nameprint(f”开始线程: {thread_name}”)self.print_timeprint(f”退出线程: {thread_name}”)# 可选函数,此处函数的代码可移到run函数内部,也可放到MyThread之外,无关紧要# 线程要做的具体事务函数,我们这里就打印两轮时间def print_time(self):count = 2thread_name = threading.current_thread.namewhile count:time.sleep(1)# 同步添加处3/4:要操作共用资源(这里即打印)时获取锁self.threading_lock.acquireprint(f”this is thread : {thread_name}”)print(f”now time is : {datetime.datetime.now.strftime(‘%Y-%m-%d %H:%M:%S %f’)}”)# 同步添加处4/4:操作完共用资源(这里即打印)后释放锁self.threading_lock.releasecount -= 1# 该类是一个演于调用MyThread的类# 其实其代码也完全可以放在if __name__ == “__main__”处class TestClass:def __init__(self):passdef main_logic(self):# 同步添加处1/4:定义一个锁对象threading_lock = threading.Lock# 创建新线程实例thread_1 = MyThread(threading_lock)thread_2 = MyThread(threading_lock)# 启动新线程thread_1.startthread_2.start# thread_1.join即当前线程(亦即主线程)把时间让给thread_1,待thread_1运行完再回到当前线程# thread_2.join即当前线程(亦即主线程)把时间让给thread_2,待thread_1运行完再回到当前线程# join方法非阻塞# 如果没对某个线程使用join方法,那么当前线程(亦即主线程)不会等待该线程执行完再结束,他会直接结束# 在多线程的进程中,主线程的地位和其他线程的地位是平等的,不会说主线程退出了就会导致整个进程,进而导致其他线程被迫终止# 自己把这两句join注释掉再运行一遍,可以更好理解这里的说法thread_1.jointhread_2.joinprint(“退出主线程”)if __name__ == “__main__”:obj = TestClassobj.main_logicView Code使用锁后输出如下:

2.3 错误的锁使用方式虽然在上一小节的代码注释中有说明,但一是自己踩了坑然后想了半天二是感觉应该还是比较典型的容易犯的问题,所以单独一节再强调一下。

总的意思就是线程同步用的锁必须要来自线程类外部而不能是来自线程类内部的自己实例化。

举个例子,比如我们有一个类叫Test其内部有一个变量a,Test类有两个实例化对象test1和test2,那么我们知道test1.a和test2.a并不是一个变量,对test1.a的任何操作都不会影响test2.a。

所以自己print顺序混乱,使用了锁还是混乱时,就要注意是不是自己的锁是在线程类内部实例化的(不要盯着网上什么print不是线程安全的、要用sys.stdout.write之类的说法想半天)。

三、线程池实现假设我们有100个任务,我们打算分10轮进行,每轮创建10个线程去处理10个任务,这是一个可行的做法,但有些粗放。

每轮创建10个线程,那么就意味着完成这100个任务前后共有100个线程的创建及消毁的动作,而如果我们创建10个线程组成线程池那么就能反复复用这10个线程,完成100个任务的前后总共只有10个线程的创建及消毁的动作。(但要注意整个过程中并不一定是每个线程都处理10个任务了,有的线程可能处理多于10个,有的线程则可能处理少于10个)

减少线程创建及消毁过程中损失的计算资源正是线程池的意义所在。

3.1 由普通单线程类改造成使用线程池的类的方法有时候我们会遇到这种情况:我们之前已经写好了一个类,但该类是单线程的我们现在机改成多线程的形式。

我们下边来看一下如何该动最少的代码,将该类改成使用线程池的形式;同时有了第二大节的知识,也直接改成线程同步的形式。

假设原始的单线程的代码形式如下:

import threadingimport timeclass TestC

Similar Posts

发表评论

邮箱地址不会被公开。 必填项已用*标注