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

本文详细介绍了Python的threading模块,包括为什么使用多线程、线程与进程的区别、并发与并行的概念,以及如何通过threading创建线程。通过三种方式展示了创建Thread对象:传入函数、可调用类实例和子类化Thread。文中还列举了threading模块中的常用函数,并提供了实例代码展示线程的启动和同步。
摘要由CSDN通过智能技术生成

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

Python多线程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解释器开始时创建的线程。

参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值