创建Thread
对象来启动一个新的线程
当Python多线程时,首先需要导入threading
模块。然后,我们可以通过创建Thread
对象来启动一个新的线程。下面是一步一步详细说明Python多线程的使用:
步骤1:导入threading
模块
import threading
步骤2:定义线程执行的任务函数
定义一个函数,作为线程执行的任务。该函数将在新的线程中被调用。
def task():
# 执行线程任务的代码
print("Hello from a thread")
步骤3:创建Thread
对象并启动线程
创建一个Thread
对象,将任务函数作为参数传递给Thread
对象,并调用start
方法启动线程。
thread = threading.Thread(target=task)
thread.start()
当调用start()
方法时,它会调用_start_new_thread()
函数,该函数位于_thread
模块中,会创建一个新的线程并调用run()
方法。 下面是threading
模块的Thread
类中的简化代码示例:
class Thread:
# ... other methods and properties
def start(self):
_start_new_thread(self._bootstrap, ())
def run(self):
pass # actual implementation of run method should be provided by the user
def _start_new_thread(function, args, kwargs=None):
# ... code for starting a new thread
在实际的Python源码中,_start_new_thread()
函数实际上是由C语言编写的,
步骤4:等待线程结束
如果希望等待线程执行完毕,可以使用join
方法。调用join
方法将会阻塞当前线程,直到目标线程执行完毕。
thread.join()
通过上面的步骤,我们可以在Python中使用多线程来执行任务。下面是几个详细说明的示例:
示例1:打印数字
import threading
def print_numbers():
for i in range(1, 6):
print(i)
# 创建线程并启动
thread = threading.Thread(target=print_numbers)
thread.start()
在上面的示例中,我们定义了一个print_numbers
函数,该函数会打印数字1到5。然后,我们创建一个线程并调用print_numbers
函数作为线程的任务,并启动线程。运行该程序,将会同时输出1到5。
示例2:计算阶乘
import threading
def factorial(num):
result = 1
for i in range(1, num + 1):
result *= i
print(f"The factorial of {num} is {result}")
# 创建多个线程并启动
threads = []
for i in range(1, 6):
thread = threading.Thread(target=factorial, args=(i,))
threads.append(thread)
thread.start()
# 等待所有线程完成
for thread in threads:
thread.join()
在上面的示例中,我们定义了一个factorial
函数,该函数会计算给定数字的阶乘,并将结果打印出来。然后,我们创建了5个线程,每个线程计算一个不同数字的阶乘。最后,我们使用join
方法等待所有线程执行完成。
这些示例演示了如何在Python中使用多线程。请注意,由于GIL的限制,Python多线程threading 并不适合CPU密集型的任务,对于IO密集型任务(如网络请求、文件读写等),多线程可以提供一定的性能优势。对于需要并行计算的任务,可以考虑使用多进程进行并行计算。
子类继承threading父类重写run方法
如果要使用多线程处理的逻辑比较复杂建议使用定义子类继承threading父类重写run方法的方式:
使用子类重写run()
方法的方式相对于使用threading.Thread(target=task)
的方式的优点之一在于更好的封装和可维护性。
当处理逻辑比较复杂时,使用子类重写run()
方法的方式可以更好地把相关的逻辑封装在一个类中,使得代码更易于理解和维护。此外,使用子类的方式可以让你更好地封装线程特定的行为和属性,并且可以在类的初始化方法__init__()
中传递参数和设置线程的相关属性。
使用子类的方式使得你可以更方便地扩展和定制线程的行为,你可以轻松地重写其他方法,创建线程专属的属性等。
虽然使用threading.Thread(target=task)
的方式也可以实现多线程,但当处理逻辑比较复杂时,代码会变得难以维护和扩展。使用子类重写run()
方法的方式可以更好地组织和封装代码,让代码更具可读性和可维护性。
那问题来了:python 的threading模块中使用threading.Thread()得到的对象调用start方法时是如何自动调用run方法的呢?
在Python中,threading
模块的Thread
类是通过Python语言实现的,而不是使用C语言编写的。实际上,threading
模块的Thread
类继承自_thread
模块中的底层原语,但它的start()
方法是在Python代码中实现的。当调用start()
方法时,它会调用_start_new_thread()
函数,该函数位于_thread
模块中,会创建一个新的线程并调用run()
方法。
下面是threading
模块的Thread
类中的源码:
class Thread:
# ... other methods and properties
def start(self):
"""Start the thread's activity.
It must be called at most once per thread object. It arranges for the
object's run() method to be invoked in a separate thread of control.
This method will raise a RuntimeError if called more than once on the
same thread object.
"""
if not self._initialized:
raise RuntimeError("thread.__init__() not called")
if self._started.is_set():
raise RuntimeError("threads can only be started once")
with _active_limbo_lock:
_limbo[self] = self
try:
_start_new_thread(self._bootstrap, ())
except Exception:
with _active_limbo_lock:
del _limbo[self]
raise
self._started.wait()
def run(self):
pass # actual implementation of run method should be provided by the user
def _start_new_thread(function, args, kwargs=None):
# ... code for starting a new thread
当然核心代码就是:
class Thread:
# ... other methods and properties
def start(self):
_start_new_thread(self._bootstrap, ())
def run(self):
pass # actual implementation of run method should be provided by the user
def _start_new_thread(function, args, kwargs=None):
# ... code for starting a new thread
在实际的Python源码中,_start_new_thread()
函数实际上是由C语言编写的,但对于Python开发者来说,start()
方法会调用run()
方法是透明的,因为这些细节都被封装好,开发者只需要重写run()
方法来定义线程的操作即可。
下面写一个使用子类继承Thread类并重写run方法的多线程使用小例子
当使用子类继承Thread
类并重写run
方法时,我们可以创建一个简单的多线程应用程序,比如同时下载多个文件。下面是一个使用子类继承Thread
类的例子:
import threading
import requests
class DownloaderThread(threading.Thread):
def __init__(self, url, filename):
super().__init__()
self.url = url
self.filename = filename
def run(self):
r = requests.get(self.url, stream=True)
with open(self.filename, 'wb') as f:
for chunk in r.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)
if __name__ == "__main__":
urls = [
'https://www.example.com/file1.pdf',
'https://www.example.com/file2.pdf',
'https://www.example.com/file3.pdf',
]
filenames = ['file1.pdf', 'file2.pdf', 'file3.pdf']
threads = []
for url, filename in zip(urls, filenames):
thread = DownloaderThread(url, filename)
threads.append(thread)
thread.start()
for thread in threads:
thread.join()
print("All downloads completed")
在上面的例子中,我们创建了一个DownloaderThread
类,它继承自Thread
类并重写了run
方法。在run
方法中,我们使用requests
库下载文件,并将每个线程负责下载一个文件。然后在主程序中,我们创建多个DownloaderThread
实例,并利用多线程同时下载多个文件。
这个例子展示了使用子类继承Thread
类并重写run
方法的多线程示例。通过这种方式,我们可以更好地封装相关的逻辑,在每个线程中处理特定的任务。
感兴趣的小伙伴,赠送全套Python学习资料,包含面试题、简历资料等具体看下方。
一、Python所有方向的学习路线
Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照下面的知识点去找对应的学习资源,保证自己学得较为全面。
二、Python必备开发工具
工具都帮大家整理好了,安装就可直接上手!
三、最新Python学习笔记
当我学到一定基础,有自己的理解能力的时候,会去阅读一些前辈整理的书籍或者手写的笔记资料,这些笔记详细记载了他们对一些技术点的理解,这些理解是比较独到,可以学到不一样的思路。
四、Python视频合集
观看全面零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。
五、实战案例
纸上得来终觉浅,要学会跟着视频一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。
六、面试宝典
简历模板![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/e609d9de17e680ccba27f351612a0860.png)
![](https://i-blog.csdnimg.cn/blog_migrate/1a47e25a112028c16d67ab764589efa6.png)