python多线程的使用与源码分析

创建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所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照下面的知识点去找对应的学习资源,保证自己学得较为全面。

img
img

二、Python必备开发工具

工具都帮大家整理好了,安装就可直接上手!img

三、最新Python学习笔记

当我学到一定基础,有自己的理解能力的时候,会去阅读一些前辈整理的书籍或者手写的笔记资料,这些笔记详细记载了他们对一些技术点的理解,这些理解是比较独到,可以学到不一样的思路。

img

四、Python视频合集

观看全面零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。

img

五、实战案例

纸上得来终觉浅,要学会跟着视频一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。

img

六、面试宝典

在这里插入图片描述

在这里插入图片描述

简历模板在这里插入图片描述
若有侵权,请联系删除
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值