1. 多线程
多线程非常重要,工作中用到的也是非常多,面试时也100%会问多线程。
关于多线程的相关知识,可以参考《计算机操作系统(第四版)》
,或者自行百度查看有关文章以及视频都可以,此处不再赘述。
2. python中的多线程
2.1
多线程优点
多线程类似于同时执行多个不同程序,多线程运行有如下优点。
(1)
使用线程可以把占据长时间的程序中的任务放到后台去处理
。
(2)
用户界面可以更加吸引人,比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度。
(3)
程序的运行速度可能加快。
(4)
在一些等待的任务
实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下我们可以释放一些珍贵的资源如内存占用等等。
2.2
一些说明
(1)
线程隶属于进程
每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
(2)
线程上下文
每个线程都有他自己的一组CPU寄存器,称为线程上下文,该上下文反映了线程上次运行该线程的CPU寄存器的状态。
(3)
重要寄存器
指令指针和堆栈指针寄存器是线程上下文中两个最重要的寄存器,线程总是在进程得到上下文中运行的,这些地址都用于标志拥有线程的进程地址空间中的内存。
2.3
python3中的线程
python3线程中常用的两个模块为:_thread
、threading
(推荐使用)。
thread 模块已被废弃。用户可以使用 threading 模块代替。所以,在 python3中不能再使用"thread" 模块。为了兼容性,python3将 thread 重命名为 "_thread"
。
3. _thread模块创建线程
调用 _thread 模块中的start_new_thread()函数来产生新线程。语法如下:
_thread.start_new_thread ( function, args[, kwargs] )
参数说明:
function
- 线程函数
args
- 传递给线程函数的参数,必须是个tuple
类型
kwargs
- 可选参数
# demo
import _thread
def print_count(threadname):
count = 0
while count < 100:
count += 1
print("%s: %d" % (threadname, count))
try:
_thread.start_new_thread(print_count, ("线程1",)) # ("线程1", )为传递给print_time()的参数
_thread.start_new_thread(print_count, ("线程2",)) # 注意, 因为参数是tuple类型, 所以传递一个参数时, 不要忘记逗号
except:
print("Error: 无法启动线程")
while True: pass
4. threading模块创建线程
4.1
两种方式
python 的 threading 模块中提供了类 Thread 用于实现多线程,用户有两种创建多线程的方式:
(1)
在线程构造函数中指定线程的入口函数。
(2)
自定义一个类,该类继承类 Thread,在自定义的类中实现 run 方法。
4.2
关键函数
主要介绍 Thread 相关的三个函数的功能。
[a].
类 Thread 的构造函数
def __init__(self, group=None, target=None, name=None, args=(), kwargs=None, *, daemon=None)
参数说明:
group
: 线程组,目前还没有实现,在此处必须是 None
target
: 线程的入口函数,线程从该函数开始执行
name
: 线程名
args
: 线程的入口函数的参数,以元组的形式传入
kwargs
: 线程的入口函数的参数,以字典的形式传入
使用 Thread 构造一个新线程时,必须指定 target 和 args 两个参数
,target 为线程的入口,args 为线程入口函数的参数。
[b].
类 Thread 的 start 方法
def start(self)
在线程对象的构造函数中 target 指定了线程入口函数,args 指定了线程入口函数的参数。线程对象的 start 方法使新线程开始执行,执行函数 target(args)。
[c].
类 Thread 的 join 方法
def join(self, timeout=None)
调用线程对象的 start 方法后,新线程开始执行函数 target(args)。调用线程对象的 join 方法,将在调用join()的地方发生阻塞(主线程阻塞)
,等待新线程执行完毕
。
4.3
两种方式举例
(1)
在线程构造函数中指定线程的入口函数
import time
import threading
def Entry(name, begin, end):
count = begin
while count < end:
count += 1
print("%s: %d" % (name, count))
t1 = threading.Thread(target=Entry, args=('thread_1', 0, 100))
t2 = threading.Thread(target=Entry, args=('thread_2', 0, 100))
t1.start()
t2.start()
t1.join() # 等待t1结束
t2.join() # 等待t2结束
(2)
自定义类继承threading.Thread类,并实现run()
import threading
def PrintCount(name, counter):
count = 0
while count < counter:
print("%s: %d" % (name, count))
count += 1
class MyThread(threading.Thread):
def __init__(self, name, counter):
threading.Thread.__init__(self)
self.name = name
self.counter = counter
def run(self):
print("开始线程: " + self.name)
PrintCount(self.name, self.counter)
print("退出线程: " + self.name)
print('')
t1 = MyThread("thread_1", 20)
t2 = MyThread("thread_2", 20)
# 首先开启线程1, 等待线程1执行完毕后, 再开启线程2
t1.start()
t1.join() # 阻塞等待线程1执行完毕
t2.start()
t2.join()
print("退出主线程.")
5. 一个案例来看看多线程在速度上的提升
# demo1
# 我们依次计算三个循环,然后看看3个全部计算完毕后的耗时统计
from datetime import datetime
def total(begin, end):
sum = 0
for item in range(begin, end):
sum += item
return sum
time0 = datetime.now()
total(1, 10000000)
total(1, 10000000)
total(1, 10000000)
time1 = datetime.now()
time = time1 - time0
print(time.microseconds) # 微秒
# demo2
# 好, 我们采用多线程的形式去看看
from datetime import datetime
import threading
def total(begin, end):
sum = 0
for item in range(begin, end):
sum += item
return sum
time0 = datetime.now()
t1 = threading.Thread(target=total, args=(1, 10000000))
t2 = threading.Thread(target=total, args=(1, 10000000))
t3 = threading.Thread(target=total, args=(1, 10000000))
t1.start()
t2.start()
t3.start()
t1.join()
t2.join()
t3.join()
time1 = datetime.now()
time = time1 - time0
print(time.microseconds) # 微秒
相比普通方式下的402233微秒,采用多线程的方式总共耗时307763微秒,因此使用多线程加快了程序的执行速度。当然,这只是一个示例型的程序。
6. 获取线程的返回值
在继承 Thread 实现多线程的方式中,将线程的返回值保存在线程对象中
,使用一个成员变量保存线程的返回值
。下面通过一个具体的例子,说明如何获取线程的返回值。
import threading
class MyThread(threading.Thread):
def __init__(self, begin, end):
threading.Thread.__init__(self)
self.result = None
self.begin = begin
self.end = end
def run(self):
self.result = 0
for i in range(self.begin, self.end):
self.result += i
t0 = MyThread(1, 51)
t1 = MyThread(51, 101)
t0.start()
t1.start()
t0.join()
t1.join()
print(t0.result) # 获取线程的执行结果
print(t1.result)
print(t0.result + t1.result)