准备在用多线程处理数据,但对全局公用数据(非self.的数据)如果用多线程来处理,就存在跑的快的线程如果对正在使用此数据的其他线程还在运算中就更改了这些全局变量数据值的情况,正在处理数据的线程因使用的原始数据被其他线程在运算过程中更改了,就将得到不到正确的计算结果,故就要在处理这些涉及全局变量数据时采用线程锁来锁定,处理完成后解除锁定,再允许其他线程处理这些全局变量。锁定数据过程中又相当于只有一个多线程了,对效率造成影响,所以全局变量的定义在多线程中应尽量少使用。下面作了一个示例,来比较用多线程处理全局变量的方法和能效比较。不锁线程时,我机器总用时(机器不同结果不同):2187毫秒,锁线程时4029毫秒,相当于锁线程是不锁线程的两倍,双线程实际每个线程有一半的时间被锁定并没有运行。不锁线程时,运算结果有非预期值出现,锁线程后没有非预期值出现。
"""
1示例:同时开启的多线程因线程处理时序不同,对共有全局变量的引用值可能是同预期值是不同的
2示例:使用线程锁,来避免1示例可能出现的情况,但会降低线程使用效率
"""
import threading
import time
from timeit import default_timer as timer
import time
from time import *
import sys
starttime=0.00 #线程起止时间
endtime=0.00
Lock = threading.Lock()
# myThread继承父类(无锁示例),并进行重写
class myThread1(threading.Thread):
# 重写父类的构造函数
count=0
lst=['类全局成员列表值=',0,'列表中的循环计数=',0]
def __init__(self, number, index):
threading.Thread.__init__(self)
self.number = number # 添加number变量
self.id = index # 添加id变量
# 重写父类中的run函数
def run(self):
if(self.id==1):
print(f"【线程开始】{self.name}")
self.task1(self.number, self.id)
elif(self.id==2):
print(f"【线程开始】{self.name}")
self.task2(self.number, self.id)
# print("【线程结束】", self.name)
# 重写父类析构函数
def __del__(self):
print("【线程销毁释放内存】", self.name)
# 自定义的函数:供线程1调用
def task1(self, number, id):
count=1
i=1
while i <= number:
#sleep(0.1)
myThread1.lst[1]=i
myThread1.lst[3]=count
myThread1.count+=1
num=myThread1.count
s=''
sleep(0.04) #因休眠期,可能存在其他跑得快的线程将此全局变量改了,即myThread.count不一定是连续的
if(myThread1.count!=num):
s='两值已不相同'
print(f'\ntask1:线程{id}中lst={myThread1.lst},线程类全局计数count={myThread1.count},对比sleep前值={num}:{s}')
i+=1
i+=1
count+=1
# 自定义的函数,供线程2调用
def task2(self, number, id):
count=1
i=1
m = 1
while i <= number:
myThread1.lst[1]=i
myThread1.lst[3]=count
myThread1.count+=1
num=myThread1.count
s=''
sleep(0.01) #因休眠期,可能存在其他跑得快的线程将此全局变量改了,即myThread.count不一定是连续的
if(myThread1.count!=num):
s='两值已不相同'
print(f'\ntask2:线程{id}中lst={myThread1.lst},线程类全局计数count={myThread1.count},对比sleep前值={num}:{s}')
i+=1
count+=1
# myThread2继承父类(有锁示例),并进行重写
class myThread2(threading.Thread):
# 重写父类的构造函数
count=0
lst=['类全局成员列表值=',0,'列表中的循环计数=',0]
def __init__(self, number, index):
threading.Thread.__init__(self)
self.number = number # 添加number变量
self.id = index # 添加id变量
# 重写父类中的run函数
def run(self):
if(self.id==1):
print(f"【线程开始】{self.name}")
self.task3(self.number, self.id)
elif(self.id==2):
print(f"【线程开始】{self.name}")
self.task4(self.number, self.id)
# print("【线程结束】", self.name)
# 重写父类析构函数
def __del__(self):
print("【线程销毁释放内存】", self.name)
# 自定义的函数:供线程2调用
def task3(self, number, id):
count=1
i=1
while i <= number:
#sleep(0.1)
Lock.acquire() # 设置线程锁
myThread2.lst[1]=i
myThread2.lst[3]=count
myThread2.count+=1
num=myThread2.count
s=''
sleep(0.04) #因休眠期,可能存在其他跑得快的线程将此全局变量改了,即myThread.count不一定是连续的
if(myThread2.count!=num):
s='两值已不相同'
print(f'\ntask3:线程{id}中lst={myThread2.lst},线程类全局计数count={myThread2.count},对比sleep前值={num}:{s}')
Lock.release() # 释放线程锁
i+=1
i+=1
count+=1
# 自定义的函数,供线程2调用
def task4(self, number, id):
count=1
i=1
m = 1
while i <= number:
Lock.acquire() # 设置线程锁
myThread2.lst[1]=i
myThread2.lst[3]=count
myThread2.count+=1
num=myThread2.count
s=''
sleep(0.01) #因休眠期,可能存在其他跑得快的线程将此全局变量改了,即myThread.count不一定是连续的
if(myThread2.count!=num):
s='两值已不相同'
print(f'\ntask4:线程{id}中lst={myThread2.lst},线程类全局计数count={myThread2.count},对比sleep前值={num}:{s}')
Lock.release() # 释放线程锁
i+=1
count+=1
if __name__ == '__main__':
print('开始示例1:开启两个线程,不锁线程......')
starttime = timer()
thread1 = myThread1(80, 1) # 创建线程thread1:
thread2 = myThread1(150, 2) # 创建线程thread2:
thread1.start() # 启动线程1
thread2.start() # 启动线程2
thread1.join() # 等待线程1
thread2.join() # 等待线程2
endtime = timer()
sumtime=(endtime-starttime)*1000
print(f'示例1运行总时间{sumtime}毫秒\n\n5秒后准备开始运行示例2:有锁线程.....')
sleep(5)
print('开始示例2:开启两个线程,锁线程......')
starttime = timer()
thread3 = myThread2(80, 1) # 创建线程thread3:
thread4 = myThread2(150, 2) # 创建线程thread4:
thread3.start() # 启动线程3
thread4.start() # 启动线程4
thread3.join() # 等待线程3
thread4.join() # 等待线程4
endtime = timer()
sumtime=(endtime-starttime)*1000
print(f'示例2运行总时间{sumtime}毫秒')