多线程
以下内容来源于《看漫画学Python》这本书,前面十几天好多内容参考过本书内容,写的挺好。
1 线程相关知识
1.1 进程
一个进程就是一个正在执行的程序,每一个进程都有自己独立的一块内存空间,一组系统资源。在进程概念中,每一个进程的内部数据和状态都是完全独立的
在Windows操作系统中,一个进程就是一个exe或者dll程序,它们相互独立,相互也可以通信。
1.2 线程
在一个进程中可以包含多个线程,多个线程共享一块内存空间和一组系统资源。所以,系统在各个线程之间切换时,开销要比进程小得多,线程被称为轻量级进程。
1.3 主线程
Python程序至少有一个线程,这就是主线程,程序在启动后由Python解释器负责创建主线程,在程序结束后由Python解释器负责停止主线程。
在多线程中,主线程负责其他线程的启动,挂起,停止等操作。其他线程被称为子线程。
2 线程模块-----------threading
Python官方提供的threading模块可以进行多线程编程。threading模块提供了多线程编程的高级API,使用起来比较简单。
在threading模块中提供了线程类Thread,还提供了很多线程相关的函数,常用如下:
active_count():返回当前处于活动状态的线程个数
current_thread():返回当前的Thread对象
main_thread():返回主线程对象。
主线程是Python解释器启动的线程。示例代码如下:
import threading # 导入threading模块t = threading.current_thread() # 获取当前线程对象
print(t.name) # 输出当前线程的名字
print(threading.active_count()) # 输出当前活动线程的个数t = threading.main_thread()
print(t.name) # 输出主线程的名字
输出结果:当前线程和主线程是同一个
3 创建子线程
创建一个可执行的子线程需要:线程对象和线程体
线程对象:线程对象是threading模块的线程类Thread或Thread子类所创建的对象。
线程体:线程体是子线程要执行的代码,这些代码会被封装到一个函数中。子线程在启动后会执行线程体。
实现线程体主要有以下两种方式:
1,自定义函数实现线程体
2,自定义线程类实现线程体
3.1 自定义函数实现线程体
创建线程Thread对象的构造方法如下:
Thread(target=None, name=None, args=())
target参数指向线程体函数,可以自定义该线程体的函数;通过name参数可以设置线程名,若省略这个参数,则系统会为其分配一个名称;args是为线程体函数提供的参数,是一个元组类型。
示例代码如下:
import threading
import timedef thread_body(): # 定义线程体函数t = threading.current_thread() # 获取当前线程对象for n in range(5): # 执行5次print('第{0}次执行线程{1}'.format(n, t.name))time.sleep(2) # 当前线程休眠两秒print('线程{0}执行完毕'.format(t.name))t1 = threading.Thread(target=thread_body) # 创建线程对象t1
t2 = threading.Thread(target=thread_body, name='MyThready') # 创建线程对象t2
t1.start() # 启动线程t1
t2.start() # 启动线程t2
输出结果:
3.2 自定义线程类实现线程体
创建一个Thread子类并重写run()方法,run()方法就是线程体函数
示例代码如下:
import threading
import timeclass SmallThread(threading.Thread):def __init__(self, name=None):super().__init__(name=name)def run(self): # 重写run()方法t = threading.current_thread() # 获取当前线程对象for n in range(5): # 执行5次print('第{0}次执行线程{1}'.format(n, t.name))time.sleep(2) # 当前线程休眠两秒print('线程{0}执行完毕'.format(t.name))t1 = SmallThread() # 创建线程对象t1
t2 = SmallThread(name='MyThready') # 创建线程对象t2
t1.start() # 启动线程t1
t2.start() # 启动线程t2
输出结果:
4 线程管理
线程管理包括: 线程创建,线程启动,线程休眠,等待线程结束,和线程停止。
前三个内容前面已涉及,以下主要是等待线程结束,线程停止的内容。
4.1 等待线程结束
一个线程(假设是主线程)需要等待另一个线程(假设是t1子线程)执行结束后才能继续执行。
示例代码如下:
import threading
import timevalue = [] # 定义一个共享变量,多个线程可以访问该变量def thread_body():print('t1线程开始...')for n in range(2):print('t1子线程执行...')value.append(n)time.sleep(2)print('t1子线程结束...')print('主线程开始执行...')
t1 = threading.Thread(target=thread_body) # 创建线程对象t1
t1.start() # 启动线程t1
t1.join() # 等待线程t1结束
print('value = {0}'.format(value))
print('主线程继续执行...')
输出结果:
若注释掉t1.join()语句,输出结果为:
4.2 线程停止
在线程结束时,线程就停止了,业务复杂时,会在线程体执行一个“死循环”。通过判断停止变量实现线程体是否持续执行“死循环”,“死循环”结束则线程体结束,线程也就结束了。
一般情况下,死循环会执行线程任务,然后休眠,再执行,再休眠,直到循环结束。
示例代码如下:
import threading
import time# 线程停止变量
isrunning = Truedef workthread_body(): # 定义工作线程体函数while isrunning:print('工作线程正在执行...')time.sleep(5)print('工作线程结束')def controlthread_body(): # 定义控制线程体函数global isrunningwhile isrunning:command = input('请输入停止指令:')if command == 'exit':isrunning = Falseprint('控制线程结束')workthread = threading.Thread(target=workthread_body) # 创建工作线程对象
workthread.start() # 启动工作线程
controlthread = threading.Thread(target=controlthread_body) # 创建控制线程对象
controlthread.start() # 启动控制线程
输出结果如下:
5 爬虫下载图片示例:
import threading
import time
import urllib.request# 线程停止变量
isrunning = Truedef workthread_body(): # 定义工作线程体函数while isrunning:print('工作线程正在执行...')download() # 调用下载图片的函数time.sleep(5)print('工作线程结束')def controlthread_body(): # 定义控制线程体函数global isrunningwhile isrunning:command = input('请输入停止指令:')if command == 'exit':isrunning = Falseprint('控制线程结束')def download(): # 定义下载图片的函数url = '此处替换图片链接' # 此处替换图片链接req = urllib.request.Request(url) # 创建请求对象with urllib.request.urlopen(req) as response: # 发起请求data = response.read()f_name = 'download.png'with open(f_name, 'wb') as f: # 打开文件f.write(data)print('图片下载成功')workthread = threading.Thread(target=workthread_body) # 创建工作线程对象
workthread.start() # 启动工作线程
controlthread = threading.Thread(target=controlthread_body) # 创建控制线程对象
controlthread.start() # 启动控制线程
输出结果: