Python多线程

目录

线程相关的知识

进程

线程

主线程

线程模块——threading

创建子线程

自定义函数实现线程体

自定义线程类实现线程体

线程管理

等待线程结束

​编辑

线程停止

下载图片示例

如果想让我们的程序同时执行多个任务,就需要使用多线程技术了 。到目前为止,我们编写的程序都是单线程的,在运行时一次只能执行一个任务。

线程相关的知识

进程

一个进程就是一个正在执行的程序,每一个进程都有自己独立的一块内存空间、一组系统资源。在进程的概念中,每一个进程的内部数据和状态都是完全独立的。 在Windows操作系统中,一个进程就是一个exe或者dll程序,它们相互独立,相互也可以通信。

线程

在一个进程中可以包含多个线程,多个线程共享一块内存空间和一组系统资源。所以,系统在各个线程之间切换时,开销要比进程小得多 ,正因如此,线程被称为轻量级进程。

主线程

Python程序至少有一个线程,这就是主线程,程序在启动后由Python解释器负责创建主线程,在程序结束后由Python解释器负责停止主线程。 在多线程中,主线程负责其他线程的启动、挂起、停止等操作。其他线程被称为子线程。

线程模块——threading

Python官方提供的threading模块可以进行多线程编程。threading模块提供了多线程编程的高级API,使用起来比较简单。 在threading模块中提供了线程类Thread,还提供了很多线程相关的函数,这些函数中常用的如下。

active_count():返回当前处于活动状态的线程个数。

current_thread():返回当前的Thread对象。

main_thread():返回主线程对象。主线程是Python解释器启动的线程。

示例代码如下:

import threading
# 当前线程对象
t=threading.current_thread() # 在运行过程中只有一个线程,就是主线程,因此当前线程是主线程
# 当前线程名
print(t.name)
#返回当前处于活动状态的线程个数
print(threading.active_count()) # 在运行过程中只有一个线程,活动状态的线程
# 当主线程对象
t=threading.main_thread() # 主线程和当前线程是同一个
#主线程名
print(t.name)

通过Python指令运行文件。

MainThread
1
MainThread

创建子线程

创建一个可执行的子线程,需要如下两个要素。

(1)线程对象:线程对象是threading模块的线程类Thread或Thread子类所创建的对象。

(2)线程体:线程体是子线程要执行的代码,这些代码会被封装到一个函数中。子线程在启动后会执行线程体。实现线程体主要有以下两种方式。

         1)自定义函数实现线程体。 2)自定义线程类实现线程体。

自定义函数实现线程体

创建线程Thread对象的构造方法如下:

Thread(target=None,name=None,args=())

target参数指向线程体函数,我们可以自定义该线程体函数;通过name参数可以设置线程名,如果省略这个参数,则系统会为其分配一个名称;args是为线程体函数提供的参数,是一个元组类型。

import threading
import time
# 线程体函数
def thread_body():
    # 当前线程对象
    t=threading.current_thread()
    for n in range(5):
        # 当前线程名
        print('第{0}次执行线程{1}'.format(n,t.name))
        # 线程休眠
        time.sleep(2) # 该函数可以使得当前线程休眠两秒,只有当前线程休眠,其他线程才有机会执行
    print('线程{0}执行完成!'.format(t.name))
# 主线程
# 创建线程对象t1
t1=threading.Thread(target=thread_body()) # 指定线程体的函数名,注意在函数名后面不要跟小括号
# 创建线程对象t2
t2=threading.Thread(target=thread_body(),name='MyThread') # 设置线程名
# 启动线程t1
t1.start()
# 启动线程t2
t2.start()

通过Python指令运行文件。

第0次执行线程MainThread
第1次执行线程MainThread
第2次执行线程MainThread
第3次执行线程MainThread
第4次执行线程MainThread
线程MainThread执行完成!
第0次执行线程MainThread
第1次执行线程MainThread
第2次执行线程MainThread
第3次执行线程MainThread
第4次执行线程MainThread
线程MainThread执行完成!

注意:从运行结果看,两个子线程是交错进行的,在多线程编程时,要注意给每个子线程执行的机会,主要是通过让子线程休眠来让当前线程暂停执行,其他线程才有机会执行。如果子线程没有休眠,则基本上在第一个子线程执行完毕后,再执行第二个子线程。

自定义线程类实现线程体

另外一种实现线程体的方式是,创建一个Thread子类并重写run()方法,run()方法就是线程体函数。

采用自定义线程类重新实现上面的示例,示例代码如下:

import threading
import time

class SmallThread(threading.Thread): # 自定义线程类,继承Thread
    def __init__(self,name=None): # 定义线程类的构造方法,name参数是线程名
        super().__init__(name=name)
    # 线程体函数
    def run(self):
       # 当前线程对象
       t=threading.current_thread()
       for n in range(5):
           # 当前线程名
           print('第{0}次执行线程{1}'.format(n,t.name))
           # 线程休眠
           time.sleep(2) # 该函数可以使得当前线程休眠两秒,只有当前线程休眠,其他线程才有机会执行
       print('线程{0}执行完成!'.format(t.name))
# 主线程
# 创建线程对象t1
t1=SmallThread()# 通过自定义线程类,创建线程对象
# 创建线程对象t2
t2=SmallThread(name='MyThread')
# 启动线程t1
t1.start()
# 启动线程t2
t2.start()

通过Python指令运行文件。

第0次执行线程Thread-1
第0次执行线程MyThread
第1次执行线程Thread-1
第1次执行线程MyThread
第2次执行线程Thread-1
第2次执行线程MyThread
第3次执行线程Thread-1
第3次执行线程MyThread
第4次执行线程MyThread
第4次执行线程Thread-1
线程Thread-1执行完成!线程MyThread执行完成!

线程管理

线程管理包括线程创建、线程启动、线程休眠、等待线程结束和线程停止,其中,线程创建、线程启动和线程休眠在前面已经用到了, 这些不再赘述。这里重点介绍等待线程结束和线程停止的内容。

等待线程结束

有时,一个线程(假设是主线程)需要等待另外一个线程(假设是 t1子线程)执行结束才能继续执行。

join()方法的语法如下:

join(timeout=None)

参数timeout用于设置超时时间,单位是秒。如果没有设置timeout, 则可以一直等待,直到结束。 使用join()方法的示例代码如下:

import threading
import time
# 共享变量
value=[]
# 线程体函数
def thread_body():
    # 当前线程对象
    print('t1子线程开始……')
    for n in range(2):
        print('t1子线程执行……')
        value.append(n)
        # 线程休眠
        time.sleep(2)
    print('t1子线程结束')
# 主线程
# 创建线程对象t1
t1=threading.Thread(target=thread_body)
# 启动线程
t1.start()
# 主线程被阻塞,等待t1线程结束
t1.join() # 在当前线程(主线程)中调用t1的join()方法,因此会导致当前线程阻塞,等待t1线程结束
print('value={0}'.format(value)) # t1线程结束,继续执行,访问并输出变量value
print('主线程继续执行……')

通过Python指令运行文件。

t1子线程开始……
t1子线程执行……
t1子线程执行……
t1子线程结束
value=[0, 1]
主线程继续执行……

从运行结果来看,在子线程t1结束后,主线程才输出变量value的内容,这说明主线程被阻塞了。 如果尝试将t1.join()语句注释掉,则输出结果如下:

t1子线程开始……value=[]
主线程继续执行……

t1子线程执行……
t1子线程执行……
t1子线程结束

从运行结果可见,子线程t1还没有结束,主线程就输出变量value的内容了。

线程停止

在线程体结束时,线程就停止了。但在某些业务比较复杂时,会在线程体中执行一个“死循环”。线程体是否持续执行“死循环”是通过判断停止变量实现的,“死循环”结束则线程体结束,线程也就结束了。 另外,在一般情况下,死循环会执行线程任务,然后休眠,再执行 ,再休眠,直到结束循环。

示例代码如下:

import threading
import time
# 线程停止变量
isrunning=True # 创建一个线程停止变量isrunning,控制线程结束
# 工作线程体函数
def workthread_body(): # 工作线程体执行一些任务
    while isrunning: # 工作线程体“死循环”
        # 线程开始工作
        print('工作线程执行中……')
        # 线程休眠
        time.sleep(5)
    print('工作线程结束。')
# 控制线程体函数
def controlthread_body(): # 控制线程体从控制台读取指令,根据指令修改线程停止变量
    global isrunning # 由于需要在线程体修改变量isrunning,因此需要将isrunning变量声明为global
    while isrunning: # 控制线程体“死循环”
        # 从键盘输入停止指令exit
        command=input('请输入停止指令:')
        if command=='exit':
            isrunning=False
            print('控制线程结束。')
# 主线程
# 创建工作线程对象workthread
workthread=threading.Thread(target=workthread_body) # 工作线程用来执行一些任务
# 启动线程workthread
workthread.start()
# 创建控制线程对象controlthread
controlthread=threading.Thread(target=controlthread_body)
# 启动线程controlthread
controlthread.start()

通过Python指令运行文件。

工作线程执行中……
请输入停止指令:工作线程执行中……
工作线程执行中……
工作线程执行中……
工作线程执行中……
工作线程执行中……
ex
请输入停止指令:请输入停止指令:工作线程执行中……
exit
控制线程结束。
工作线程结束。

下载图片示例

这个网络爬虫程序每隔一段时间都会执行一次下载图片任务,在下载任务完成后,休眠一段时间再执行。这样反复执行,直到爬虫程序停止。

示例参考代码如下:

import threading
import time
import urllib.request
# 线程停止变量
isrunning=True # 创建一个线程停止变量isrunning,控制线程结束
# 工作线程体函数
def workthread_body(): # 工作线程体执行一些任务
    while isrunning: # 工作线程体“死循环”
        # 线程开始工作
        print('工作线程执行下载任务……')
        download() # 在工作线程中执行下载任务,这个下载任务每5秒调用一次
        # 线程休眠
        time.sleep(5)
    print('工作线程结束。')
# 控制线程体函数
def controlthread_body(): # 控制线程体从控制台读取指令,根据指令修改线程停止变量
    global isrunning # 由于需要在线程体修改变量isrunning,因此需要将isrunning变量声明为global
    while isrunning: # 控制线程体“死循环”
        # 从键盘输入停止指令exit
        command=input('请输入停止指令:')
        if command=='exit':
            isrunning=False
            print('控制线程结束。')
def download(): # 下载函数,由工作线程调用
    url='http://localhost:8080/NoteWebService/logo.png'
    req=urllib.request.Request(url)
    with urllib.request.urlopen(url) as response:
        data=response.read()
        f_name='download.png'
        with open(f_name,'wb') as f:
            f.write(data)
            print('下载文件成功')
# 主线程
# 创建工作线程对象workthread
workthread=threading.Thread(target=workthread_body) # 工作线程用来执行一些任务
# 启动线程workthread
workthread.start()
# 创建控制线程对象controlthread
controlthread=threading.Thread(target=controlthread_body)
# 启动线程controlthread
controlthread.start()

本示例从服务器下载图片,因此需要参考前面启动Web服务器, 然后通过Python指令运行文件。

  • 42
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

互联网的猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值