Python多线程threading模块(一)创建线程

活动地址:CSDN21天学习挑战赛

Python多线程threading模块(一)创建线程

文章目录

  • Python多线程threading模块(一)创建线程
  • 引入
    • 为什么使用多线程
    • 并行和并发
  • 创建线程
    • threading模块简介
    • Thread类
    • 使用Thread类创建线程
      • 创建 Thread 的实例,传给它一个函数。
      • 创建 Thread 的实例,传给它一个可调用的类实例。
      • 派生 Thread 的子类,并创建子类的实例
  • threading模块中常用的模块级函数
  • 参考资料

引入

为什么使用多线程

过去,计算机程序都是串行执行的,即同一时刻,只有一个任务在执行。这种方式,对于解决问题的效率而言,不是太高。随着计算机多核技术的发展,同时处理多个任务以节省时间,进而提高工作效率成为热点。

  • 进程(有时称为重量级进程):计算机程序只是存储在磁盘上的可执行二进制(或其他类型)文件。而所谓进程,就是正在执行中的程序。每个进程在内存空间中都拥有自己的地址空间、内存、数据栈以及其他用于跟踪执行的辅助数据。

  • 线程(有时称为轻量级进程):线程是在同一个进程中执行的,一个进程中可以有多个线程。

并行和并发

  • 并发: 是指同一时刻发起多个任务,但这些任务不一定同时执行。

  • 并行:是指同一时刻同时执行多个任务。这里强调同时执行。

并发是指一次处理多件事。

并行是指一次做多件事。

二者不同,但是有联系。

一个关于结构,一个关于执行。

并发用于制定方案,用来解决可能(但未必)并行的问题。

                    ——Rob Pike

                    Go 语言的创造者之一

单核计算机只存在并发的概念,因为不可能同时执行多个任务。

创建线程

threading模块简介

首先列出,threading模块中所有可用的对象,后面介绍时,只记录Thread对象的使用。

对 象 描 述
Thread 表示一个执行线程的对象
Lock 锁原语对象(和 thread 模块中的锁一样)
RLock 可重入锁对象,使单一线程可以(再次)获得已持有的锁(递归锁)
Condition 条件变量对象,使得一个线程等待另一个线程满足特定的“条件”,比如改变状态或某个数据值
Event 条件变量的通用版本,任意数量的线程等待某个事件的发生,在该事件发生后所有线程将被激活
Semaphore 为线程间共享的有限资源提供了一个“计数器”,如果没有可用资源时会被阻塞
BoundedSemaphore 与 Semaphore 相似,不过它不允许超过初始值
Timer 与 Thread 相似,不过它要在运行前等待一段时间
Barrier 创建一个“障碍”,必须达到指定数量的线程后才可以继续

Thread类

  • Thread类的成员变量
成员变量 描述
name 线程名
ident 线程的标识符
daemon 布尔标志,表示这个线程是否是守护线程
  • Thread类的对象方法
对象方法 描述
init(group=None, tatget=None, name=None, args=(), kwargs ={}, verbose=None, daemon=None) 实例化一个线程对象,需要有一个可调用的 target,以及其参数 args或 kwargs。还可以传递 name 或 group 参数,不过后者还未实现。此外 , verbose 标志也是可接受的。而 daemon 的值将会设定thread.daemon 属性/标志
start() 开始执行该线程
run() 定义线程功能的方法(通常在子类中被应用开发者重写)
join (timeout=None) 直至已经启动的线程终止之前一直阻塞;除非给出了 timeout(秒),否则会一直阻塞
getName() 返回线程名
setName (name) 设定线程名

使用Thread类创建线程

主要有三种方式去创建线程:较常用的是第一种和第三种

• 创建 Thread 的实例,传给它一个函数。

• 创建 Thread 的实例,传给它一个可调用的类实例。

• 派生 Thread 的子类,并创建子类的实例。


创建 Thread 的实例,传给它一个函数。

以下代码有些臃肿,我们只需要关注main函数中的第一个for循环中的循环体即可。

#filename: mtsleepC.py
import threading
from time import sleep, ctime

loops = [4, 2] #将睡眠时间常量保存到列表中

def loop(nloop, nsec):
    print('start loop', nloop, ' at:', ctime())
    sleep(nsec)
    print('loop', nloop, ' done at:', ctime())

def main():
    print('main starting at:', ctime())
    threads = []
    nloops = range(len(loops))

    for i in nloops:
        t = threading.Thread(target=loop, args=(i, loops[i]))
        threads.append(t)

    for i in nloops:
        threads[i].start() #开始执行threas[i]线程

    for i in nloops:
        threads[i].join() #如果threads[i]没有执行结束,则阻塞

    print('all Done at :', ctime())

if __name__=='__main__':
    main()

执行结果:每次运行,结果会有些许不同

main starting at: Sun Aug  7 22:56:28 2022
start loop 0  at: Sun Aug  7 22:56:28 2022
start loop 1  at: Sun Aug  7 22:56:28 2022
loop 1  done at: Sun Aug  7 22:56:30 2022
loop 0  done at: Sun Aug  7 22:56:32 2022
all Done at : Sun Aug  7 22:56:32 2022

说明:只关注核心代码部分

    for i in nloops:
        t = threading.Thread(target=loop, args=(i, loops[i]))
        threads.append(t)

对于上面的代码:t = threading.Thread(target=loop, args=(i, loops[i])),就是将Thread类进行实例化。有了一个Thread类就相当于有了一个线程,但是这个线程不会立刻执行,需要显示调用Thread.start()方法,来启动线程,执行相关任务,而这里的任务就是在初始化时,传递的参数target=loop,后面的args=()就是函数loop()所需要的参数。


创建 Thread 的实例,传给它一个可调用的类实例。

同样,基于上面程序的代码进行小的修改。这里只需要关注main函数中的第一个for循环的循环体以及新建的类ThreadFunc


#filename: mtsleepD.py

import threading
from time import sleep, ctime

loops = [4, 2] #将睡眠时间常量保存到列表中

class ThreadFunc(object):

    def __init__(self, func, args, name=' '):
        self.name = name
        self.func = func
        self.args = args

    def __call__(self):
        self.func(*self.args)

def loop(nloop, nsec):
    print('start loop', nloop, ' at:', ctime())
    sleep(nsec)
    print('loop', nloop, ' done at:', ctime())

def main():
    print('main starting at:', ctime())
    threads = []
    nloops = range(len(loops))

    for i in nloops:
        t = threading.Thread(target=ThreadFunc(loop, (i, loops[i]), loop.__name__))
        threads.append(t)

    for i in nloops:
        threads[i].start()

    for i in nloops:
        threads[i].join()

    print('all Done at :', ctime())

if __name__=='__main__':
    main()


这里运行结果省略。只关注核心代码部分:

class ThreadFunc(object):

    def __init__(self, func, args, name=' '):
        self.name = name
        self.func = func
        self.args = args

    def __call__(self):
        self.func(*self.args)

for i in nloops:
    t = threading.Thread(target=ThreadFunc(loop, (i, loops[i]), loop.__name__))
    threads.append(t)

说明:

这里,我们同样是实例化了一个Thread类的对象,但是这次的target是另外一个类ThreadFunc的实例对象。这个被作为target的类需要是可被调用的,即该类需要定义__call__()方法,当我们使用threading.Thread()创建了一个Thread类的实例对象时,便得到了一个线程,该线程调用其start()方法后,线程会自动调用ThreadFunc类的__call()__方法体。


派生 Thread 的子类,并创建子类的实例


#filename: mtsleepE.py

import threading
from time import sleep, ctime

loops = [4, 2] #将睡眠时间常量保存到列表中

class MyThread(threading.Thread):

    def __init__(self, func, args, name=' '):
        threading.Thread.__init__(self)
        self.name = name
        self.func = func
        self.args = args

    def run(self):
        self.func(*self.args)

def loop(nloop, nsec):
    print('start loop', nloop, ' at:', ctime())
    sleep(nsec)
    print('loop', nloop, ' done at:', ctime())

def main():
    print('main starting at:', ctime())
    threads = []
    nloops = range(len(loops))

    for i in nloops:
        t = MyThread(loop, (i, loops[i]), loop.__name__)
        threads.append(t)

    for i in nloops:
        threads[i].start()

    for i in nloops:
        threads[i].join()

    print('all Done at :', ctime())

if __name__=='__main__':
    main()


同样,只关注核心代码:

class MyThread(threading.Thread):

    def __init__(self, func, args, name=' '):
        threading.Thread.__init__(self)
        self.name = name
        self.func = func
        self.args = args

    def run(self):
        self.func(*self.args)


    for i in nloops:
        t = MyThread(loop, (i, loops[i]), loop.__name__)
        threads.append(t)

说明:

声明了一个类MyThread,该类继承自threading.Thread。重写了run(self)方法,main函数的第一个for循环中,将MyThread进行实例化,进而获得了新建的线程对象,随后调用的MyThread.start()方法启动线程,线程便会自动调用MyThread.run()中的方法体,所以需要重写threading.Thread中的run()方法


threading模块中常用的模块级函数

threading.active_count() 返回当前存活的 Thread 对象的数量。 返回值与 enumerate() 所返回的列表长度一致。
threading.current_thread() 返回当前对应调用者的控制线程的 Thread 对象。如果调用者的控制线程不是利用 threading 创建,会返回一个功能受限的虚拟线程对象。
threading.get_ident() 返回当前线程的 “线程标识符”。它是一个非零的整数。它的值没有直接含义,主要是用作 magic cookie,比如作为含有线程相关数据的字典的索引。线程标识符可能会在线程退出,新线程创建时被复用。
threading.enumerate() 返回当前所有存活的 Thread 对象的列表。 该列表包括守护线程以及 current_thread() 创建的空线程。 它不包括已终结的和尚未开始的线程。 但是,主线程将总是结果的一部分,即使是在已终结的时候。
threading.main_thread() 返回主 Thread 对象。一般情况下,主线程是Python解释器开始时创建的线程。

参考资料

  • threading — 基于线程的并行 — Python 3.10.6 文档

  • (6条消息) 100天精通Python(进阶篇)——第37天:多线程(threading模块)_无 羡ღ的博客-CSDN博客

  • Python核心编程(第3版)

你可能感兴趣的:(python,其他,python)