1. Python多线程
python3中常用的线程模块为:_thread(Python2中的thread)、threading(推荐)
线程池:ThreadPoolExecutor
2. 使用线程
第一种方式:
_thread.start_new_thread(function,args[,kwargs])
function:线程函数
args:传递给线程函数的参数,必须是tuple(元组)类型
kwargs:可选参数
第二种方式
import threading
import time
import traceback
class MyThread(threading.Thread):
def __init__(self, thread_id, thread_name, counter):
threading.Thread.__init__(self)
self.thread_id = thread_id
self.thread_name = thread_name
self.counter = counter
def run(self):
print_time(self.thread_name, 1, self.counter)
def print_time(thread_name, delay, counter):
while counter:
time.sleep(delay)
print('%s : %s ' % (thread_name, time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())))
counter -= 1
thread1 = MyThread(1, 'time_thread_1', 5)
thread1.start()
3. thread内部方法
1.threading.currentThread():返回当前的线程变量;
2.threading.enumerate():返回当前正在运行的线程list,正在运行是指线程启动后、结束前;
3.threading.activeCount():返回当前正在运行的线程数,与len(threading.enumerate())相同
4.run():用以表示线程互动的方法
5.start():启动线程
6.join([time]):等待线程终止,阻塞线程,直至join被调用或者线程中止、正常退出或者异常,或者超时[time]
7.isAlive():返回线程是否存活
8.getName():线程名
9.setName():设置线程名
4. 线程同步
如果多个线程同时修改某个数据可能会导致数据的正确性,所以需要对线程进行同步处理,使用 Thread 对象的 Lock 和 Rlock 可以实现简单的线程同步,这两个对象都有 acquire 方法和 release 方法,对于那些需要每次只允许一个线程操作的数据,可以将其操作放到 acquire 和 release 方法之间。
如下实例,多个线程同时进行对某个数据进行自增,未加锁时结果会怎么样?
import threading
class MyThread(threading.Thread):
def __init__(self, thread_id, thread_name):
threading.Thread.__init__(self)
self.thread_id = thread_id
self.thread_name = thread_name
def run(self):
#获取锁
# threadLock.acquire()
global number
for i in range(1, 1000000):
number += 1
#释放锁
# threadLock.release()
threadLock = threading.Lock()
number = 1
thread = MyThread(1, 'thread')
thread.start()
thread2 = MyThread(2, 'thread2')
thread2.start()
计算结果=1999999 ? 1313434
Process finished with exit code 0
如果把加锁的注释去掉会怎么样呢?
def run(self):
#获取锁
threadLock.acquire()
global number
for i in range(1, 1000000):
number += 1
#释放锁
threadLock.release()
计算结果=1999999 ? 1999999
Process finished with exit code 0
5. 线程池
1. 线程池介绍
线程池在系统启动时即创建大量空闲的线程,程序只要将一个函数提交给线程池,线程池就会启动一个空闲的线程来执行它。当该函数执行结束后,该线程并不会死亡,而是再次返回到线程池中变成空闲状态,等待执行下一个函数。
使用线程池可以有效地控制系统中并发线程的数量。当系统中包含有大量的并发线程时,会导致系统性能急剧下降,甚至导致 Python 解释器崩溃,而线程池的最大线程数参数可以控制系统中并发线程的数量不超过此数。
2.线程池的使用
线程池的基类是 concurrent.futures 模块中的 Executor,Executor 提供了两个子类,即 ThreadPoolExecutor 和 ProcessPoolExecutor,其中 ThreadPoolExecutor 用于创建线程池,而 ProcessPoolExecutor 用于创建进程池。
Exectuor 提供了如下常用方法:
submit(fn, *args, **kwargs):将 fn 函数提交给线程池。*args 代表传给 fn 函数的参数,*kwargs 代表以关键字参数的形式为 fn 函数传入参数。
map(func, *iterables, timeout=None, chunksize=1):该函数类似于全局函数 map(func, *iterables),只是该函数将会启动多个线程,以异步方式立即对 iterables 执行 map 处理。
shutdown(wait=True):关闭线程池。
submit 方法会返回一个 Future 对象
Future 提供了如下方法:
cancel():取消该 Future 代表的线程任务。如果该任务正在执行,不可取消,则该方法返回 False;否则,程序会取消该任务,并返回 True。
cancelled():返回 Future 代表的线程任务是否被成功取消。
running():如果该 Future 代表的线程任务正在执行、不可被取消,该方法返回 True。
done():如果该 Funture 代表的线程任务被成功取消或执行完成,则该方法返回 True。
result(timeout=None):获取该 Future 代表的线程任务最后返回的结果。如果 Future 代表的线程任务还未完成,该方法将会阻塞当前线程,其中 timeout 参数指定最多阻塞多少秒。
exception(timeout=None):获取该 Future 代表的线程任务所引发的异常。如果该任务成功完成,没有异常,则该方法返回 None。
add_done_callback(fn):为该 Future 代表的线程任务注册一个“回调函数”,当该任务成功完成时,程序会自动触发该 fn 函数。
在用完一个线程池后,应该调用该线程池的 shutdown() 方法,该方法将启动线程池的关闭序列。调用 shutdown() 方法后的线程池不再接收新任务,但会将以前所有的已提交任务执行完成。当线程池中的所有任务都执行完成后,该线程池中的所有线程都会死亡。
from concurrent.futures import ThreadPoolExecutor
import threading, time
class WorkThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self) -> None:
print('start run task')
time.sleep(2)
print('task finish\n')
return '线程类执行完成'
def test(thread_name):
print(thread_name, threading.current_thread().name)
time.sleep(5)
print('任务完成\n')
return '线程方法执行完成'
f __name__ == '__main__':
thread_pool = ThreadPoolExecutor(5)
futures = []
for i in range(7):
thraed = WorkThread()
# sumit(方法名,参数)
future1 = thread_pool.submit(thraed.run)
future2 = thread_pool.submit(test, i)
futures.append(future1)
futures.append(future2)
def get_call_back(future):
# 监听任务执行结果,当前线程一直阻塞知道有结果,但是不阻塞主线程
print(future.result())
for future in futures:
#添加监听
future.add_done_callback(get_call_back)
print('main thread')
map执行线程
启动三个线程
thread_pool.map(test, (2,3,4))