多线程
之前对多进程的知识的总结:多进程
线程比进程单位更小,线程是一个基本的CPU执行单元。线程必须在某个进程中执行,一个进程可包含多个线程,但是只能有一个主线程。在多线程中,共享同个地址空间、打开的文件等资源;在多进程中,共享物理内存、、磁盘、打印机等资源。其中,线程按照作用不同可分为主线程、子线程、守护线程(后台线程)以及前台线程等。
在Python标准库中,提供的模块有thread和threading。其中,thread是低级模块,threading是高级模块,对thread进行了封装。
- 使用threading模块创建多线程
同创建进程一样,threading模块中也提供两种方法创建多线程。分别是传入函数创建Thread实例和直接从threading.Thread中继承并创建线程类。
测试程序threading_demo.py
# -*- coding: utf-8 -*-
import threading
import random, time
def run_thread(nums):
print("当前线程 %s 正在运行..." % threading.current_thread().name)
for num in nums:
print("线程 %s 读取数据 %s" % (threading.current_thread().name, num))
time.sleep(random.random())
print("线程 %s 结束..." % threading.current_thread().name)
class MyThread(threading.Thread):
def __init__(self, name, nums):
threading.Thread.__init__(self, name=name)
self.nums = nums
def run(self):
print("当前线程 %s 正在运行..." % threading.current_thread().name)
for num in self.nums:
print("线程 %s 读取数据 %s" % (threading.current_thread().name, num))
time.sleep(random.random())
print("线程 %s 结束..." % threading.current_thread().name)
if __name__ == '__main__':
print("当前线程 %s 正在运行..." % threading.current_thread().name)
thread1 = threading.Thread(target=run_thread, args=([i for i in range(1, 4)], ))
thread2 = threading.Thread(target=run_thread, args=([i for i in range(4, 7)], ))
thread1.start()
thread2.start()
thread1.join()
thread2.join()
thread3 = MyThread(name='thread3', nums=[i for i in range(1,4)])
thread4 = MyThread(name='thread4', nums=[i for i in range(4,7)])
thread3.start()
thread4.start()
thread3.join()
thread4.join()
print("线程 %s 结束..." % threading.current_thread().name)
- 线程同步
在多线程中,线程同步是很为重要的。这是为了防止多个线程同时对同一个数据进行了修改。在Thread对象中,Lock和RLock可以实现简单的线程同步,通过将数据操作放入acquire和release操作中。Lock和RLock的主要区别在于RLock可以多次执行acquire操作,但是有多少个acquire操作之后就需要有多少个release操作,如果对Loak执行两次acquire操作则会发生死锁。
测试程序threading_synchronization.py
import threading
import random, time
def run_thread():
global num
for i in range(4):
rlock.acquire()
print("线程 %s 已锁定,此时 num = %s"
%(threading.current_thread().name, num))
num = random.random()
rlock.release()
print("线程 %s 已解锁,此时 num = %s"
%(threading.current_thread().name, num))
if __name__ == '__main__':
print("当前线程 %s 正在运行..." % threading.current_thread().name)
rlock = threading.RLock()
num = random.random()
thread1 = threading.Thread(target=run_thread)
thread2 = threading.Thread(target=run_thread)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print("线程 %s 结束..." % threading.current_thread().name)
需要注意的是,在使用CPython解释器的时候,会存在全局解释器锁GIL的概念。该锁主要是为了在不同线程同时访问一数据时,对数据提供保护机制。这隐含着说明任何python程序,在任何时候都只有一个线程在执行,而不管处理器的数量。正是因为这把锁的存在,所以对于IO密集型的任务,使用多线程更快,而对于CPU密集型的任务,可以通过使用多进程代替多线程,避开GIL。而在CPython解释器中之所以不移除GIL,主要是移除之后解释器的执行效率会更低,同时使用GIL的话会使得初学者的门槛更低。