博客
关于我
Python--10、线程
阅读量:796 次
发布时间:2023-03-08

本文共 3113 字,大约阅读时间需要 10 分钟。

线程与多线程编程

线程和多线程编程在计算机科学中是一个深奥而广泛讨论的话题。每个进程里都有一个控制线程,进程不是一个执行单位,线程才是执行单位,而进程则是资源单位,资源隔离的关键。如果说进程是资源的分配单位,那么线程则是执行的基本单元。这意味着在同一进程内的多个线程可以共享该进程的资源,但彼此之间却有自己的独立执行环境和内存空间。

多线程的基本概念

多线程编程的核心在于进程内的多个线程能够同时执行任务,共享同一进程的地址空间。与进程相比,线程的创建速度更快,因为线程不需要复制整个进程的内存空间。一个进程内的多个线程可以同时运行,充分利用多核CPU的计算资源。

多线程的创建方式

在Python中,可以通过两种方式创建多线程:

  • 使用 threading 模块的 Thread 方法:
  • import threadingt = threading.Thread(target=module.args_)t.start()t.join()
    1. 自定义一个类继承自 threading.Thread,并重定义初始化方法:
    2. import threadingclass MyThread(threading.Thread):    def __init__(self, target, args):        super().__init__()        self.target = target        self.args = args        self.start()t = MyThread(module, args_)t.start()t.join()

      多线程的独特性

      需要注意的是,多线程共享同一个进程的地址空间,这意味着它们可以直接访问同一段内存。这种共享机制让多线程编程更加高效,但也需要注意资源竞争问题。在多线程环境下,为了避免资源竞争和死锁,通常需要使用互斥锁。

      全局解释器锁(GIL)

      在cpython中,全局解释器锁(GIL)是一个独特的机制。GIL的作用是确保在多线程环境中,Python解释器的C层代码不会被并行执行。这意味着即便有多个线程在运行,解释器的代码执行仍然是顺序进行的。GIL的引入是为了确保解释器的安全性和一致性,特别是在垃圾回收机制中。

      GIL的实现机制包括以下几个方面:

    3. GIL的启动:每当一个Python进程启动时,除了主线程外,还会启动一个垃圾回收线程。为了确保垃圾回收和其他线程不会同时访问解释器的C代码,GIL会被锁定。

    4. GIL的释放:在离开一个函数(比如sys._getframe())时,GIL会被释放。这意味着在这种情况下,其他线程可以继续执行它们的代码。

    5. GIL的影响:由于GIL的存在,Python在多核环境中的并行执行效率并不会显著提高。这也是为什么Python多核性能优化更多依赖于多进程而不是多线程的原因。

    6. 守护线程

      在多线程环境中,守护线程的概念与守护进程类似。守护线程的主要作用是确保应用程序在后台运行,即使主线程已经退出,其他线程也不会立即终止应用程序。与守护进程不同的是,在进程内,当所有非守护线程和守护线程都退出时,主线程才会终止。

      死锁与递归锁

      在多线程编程中,死锁和递归锁是两个常见的概念。

      死锁

      死锁是多个线程在等待对方完成的资源而无法继续执行的状态。典型的死锁场景是一个线程占有一个锁,而另一个线程想要占有同一个锁,但需要等待第一个线程释放锁才能继续执行。例如:

      线程1先占有锁A,想要占有锁B;

      线程2先占有锁B,想要占有锁A。
      这样两个线程就会死锁,无法继续执行。

      递归锁

      递归锁(Reentrant Lock)是一种锁的设计,允许多个线程同时持有同一锁,但每次只能有一个线程可以修改锁的状态。递归锁通过计数器的方式实现:

      • 当线程尝试获取锁时,计数器增加。
      • 当线程释放锁时,计数器减少。
      • 只有当计数器归零时,其他线程才能再次尝试获取锁。

      这种设计允许线程在递归调用自身时继续持有锁,直到递归返回后才释放锁。

      信号量

      信号量(Semaphore)是一种同步机制,可以控制同一时间有多少线程可以执行。与线程池不同,信号量用于管理线程的数量限制。在Python中,可以使用threading模块中的信号量实现:

      import threadingfrom threading import Semaphoresem = Semaphore(5)  # 允许最多5个线程同时执行with sem:    # 线程可以在这里执行    pass

      信号量的主要作用是确保在多线程环境中,资源不会被过度利用,同时防止资源耗尽。

      线程池

      线程池是一种高效的多线程管理机制,通过设置最大线程数量,自动管理线程的启动和停止。在Python中,可以使用threadpoolexec来创建线程池:

      import threadingfrom concurrent.futures import ThreadPoolExecutordef my_task():    passexecutor = ThreadPoolExecutor(max_workers=10)executor.submit(my_task)executor.shutdown(wait=True)

      线程池默认的最大线程数是CPU核数的5倍,这可以根据具体需求进行调整。

      多线程池的应用场景

      线程池(Pool)和信号量(Semaphore)是并发编程中的两个重要概念。线程池适用于需要同时执行多个任务且任务规模较大时使用,而信号量则用于控制线程的数量。在小规模的任务中,线程池往往更高效,因为它可以自动管理线程的启动和终止。

      定时器

      定时器是一种常见的并发编程需求,可以通过threading模块中的timer实现:

      import threadingfrom threading import Timerdef on_timeout():    passtimer = Timer(60, on_timeout)timer.start()timer.join()

      线程队列

      线程队列(Queue)是一种高效的数据传输机制,可以在多线程环境中安全地进行数据的生产和消费。Python提供了queue模块,其中包括LifoQueue和PriorityQueue等类型:

      from queue import Queue, LifoQueue, PriorityQueueq = Queue()q.put( ('data', 1) )q.put( ('data', 2) )while not q.empty():    item = q.get()    print(item)

      事件

      事件(Event)是一种同步机制,常用于多线程编程中。Python的event模块提供了简单易用的事件实现:

      import eventdef my_event_listener():    passe = event.Event()e.bind(my_event_listener)e.set()e.wait()e.clear()

      并发编程的核心要素

      并发编程的高级主题包括:

    7. 进程与线程:进程是资源的分配单位,线程是执行的基本单元。
    8. 生产者与消费者模型:这是多线程编程的经典模式。
    9. GIL与互斥锁:GIL是cpython的独特机制,而互斥锁则是用户定义的锁机制。
    10. 线程池与回调机制:线程池是一种高效的多线程管理方式。
    11. 通过合理运用这些概念,可以在多线程环境中高效地实现任务并发,充分发挥多核硬件的性能。

    转载地址:http://swlfk.baihongyu.com/

    你可能感兴趣的文章