1.关于python多线程模块
我们知道线程是任务最小的执行单元,Python的标准库提供了两个模块:_thread和threading,_thread是低级模块,threading是高级模块,对_thread进行了封装。绝大多数情况下,只需要使用threading模块就好。
2.使用threading模块创建线程
相关资料: https://www.cnblogs.com/hiwuchong/p/8673183.html
语法如下:
Thread([group [, target [,name [, args [, kwargs]]]]])
group:值为None,为后续版本保留
target: 表示一个可调用对象,线程启动时,run()方法调用此对象,默认值为None,表示不调用任何对象
name:表示当前线程名称,默认创建一个名为"Thread-N"格式的唯一名称
args:表示传递给target()函数的参数元组。
kwargs:表示传递给target()参数的参数字典
ps:
*args用来将参数打包成tuple给函数体调用,如fun(1)
**kwargs打包关键字参数成dict给函数体调用,如fun(a=2),区别详请见:https://www.cnblogs.com/yunguoxiaoqiao/p/7626992.html
Thread类和Process进程类方法基本相同。
实例如下
# _*_ coding: UTF-8 _*_
import threading,time
def process():
for i in range(3):
time.sleep(1)
# 输出线程名字
print('thread name is %s' % threading.current_thread().name)
if __name__ == '__main__':
print('主线程开始:%s'%threading.current_thread().name)
threads = [threading.Thread(target=process) for i in range(4)] #创建4个线程
for t in threads: # 开启线程
t.start()
#print(threading.enumerate()) #获得当前所有线程
for i in threads:
t.join() # 等待子线程结束
print('主线程结束:%s'%threading.current_thread().name)
#print("线程的数量是:", len(threading.enumerate()))
print("当前线程:", threading.enumerate())
结果如下所示,除了主线程MainThread外,总共4个线程,每个线程运行3次,发现该结果咋一看有点奇怪,倒数第4行主线程结束了,子线程还在运行,这与join阻塞的知识点似乎不符合,实际上是这么理解,主线程并没有结束,只是恰好打印了那句"主线程结束"的话,通过threading.enumerate()查看当前线程也可证明MainThread主线程还在
见此解释:https://blog.csdn.net/fivedoumi/article/details/51863931
3.使用Thread子类创建线程
通过定义一个子类,使其继承threading.thread来创建线程.
例子如下:
# _*_ coding: UTF-8 _*_
import threading,time
class SubThread(threading.Thread):
def run(self): # 重写run函数
for i in range(3):
time.sleep(1)
msg = "子线程" + self.name + "执行,i=" +str(i) #name为当前线程名称
print(msg)
if __name__ == '__main__':
print('---主线程开始---')
t1 = SubThread() # 创建子线程
t2 = SubThread()
t1.start() # 启动子线程
t2.start()
t1.join() # 等待子线程,所有子线程结束后,主线程才结束
t2.join()
print('主线程结束')
4.线程间通信
进程是不能共享信息的,而线程是可以共享信息的,即一个全局变量可以被多个线程修改。如下例子:
# _*_ coding: UTF-8 _*_
from threading import Thread
def plus():
print('加线程开始')
global g_num
g_num += 5
print('g_num is %d' % g_num)
print('加线程结束')
def minus():
print('减线程开始')
global g_num
g_num -= 3
print('g_num is %d' % g_num)
print('减线程开始')
g_num = 2
if __name__ == '__main__':
print('主线程开始')
print('g_num is %d' % g_num)
t1 = Thread(target=plus) # 实例化线程t1
t2 = Thread(target=minus) # 实例化线程t2
t1.start() # 开启线程
t2.start()
t1.join()
t2.join()
print('主线程结束')
结果如下:即 2+5-3
5.线程间同步之互斥锁
从上个例子中可以看出线程可以对全局变量值任意改动,可能会造成多个线程对同一个全局变量的混乱操作。
可用互斥锁(Mutex),防止多个线程同时写一块内存区域。互斥锁为资源引入一个状态:锁定和非锁定,某个线程要更改数据时,先将其锁定,此时资源状态为“锁定”,别的线程不能更改,直到该线程释放资源,状态为“非锁定”时,其它线程才能锁定该资源。例子如下:
# _*_ coding: UTF-8 _*_
from threading import Thread,Lock #引入线程和锁模块
import time
n = 50 # 100张门票
def task():
global n
mutex.acquire()
temp = n
time.sleep(0.1)
n = temp - 1
print('购买成功,剩余%d张电影票'% n)
mutex.release()
if __name__ == "__main__":
mutex = Lock() #实例化Lock类
t_l = []
for i in range(10):
t = Thread(target=task) # 实例化线程
t_l.append(t) # 线程放入列表
t.start()
for j in t_l:
j.join() # 等待子线程结束
结果如下:
如果没用锁,则结果如下:线程同时操作变量后果
因此在多线程开发中,对于需要写的全局变量,为防止数据混乱,通常使用互斥锁
Reference
《Python从入门到项目实践》明日科技