希望实现下面效果
{‘data’: [{‘OuterThreadResult1’: {‘innerResult’: [‘in1’, ‘in2’]}}, {‘OuterThreadResult2’: {‘innerResult’: [‘in1’, ‘in2’]}}, {‘OuterThreadResult3’: {‘innerResult’: [‘in1’, ‘in2’]}}, {‘OuterThreadResult4’: {‘innerResult’: [‘in1’, ‘in2’]}}]}
最简单的方法自然是内层多线程写完后,外层直接循环调用内层多线程,但这种方法会拖慢程序运行速度,这里不做赘述。
第二种方法是外层多线程也考虑用多线程实现,起名外层多线程:
import threading
"""实现内层线程向列表里追加元素,外层线程调用内层线程,其中外层线程调用1次,内层线程会调用4次(4个内层线程)"""
def inner_thread_function(val):
'''
:param val:
:return: 向内层线程列表中(innerResult:全局变量)添加值
'''
innerResult.append(val)
def inner_thread():
"""内层线程"""
threads = [] # 存放线程
l1=["in1","in2"]
global innerResult
innerResult = []
for val in l1:
# 创建子线程并存放至列表threads
l = threading.Thread(target=inner_thread_function, args=(val,))
threads.append(l)
for t in threads:
# 开启线程
t.start()
for t in threads:
# 主线程等待子线程执行
t.join()
return {"innerResult": innerResult}
def outer_thread_function(val):
'''
在这里调用内层多线程
:param val:
:return:向外层线程列表(OuterThreadResult:全局变量列表)中调用内层线程,并添加内层线程返回值
'''
data = inner_thread()
OuterThreadResult.append({"OuterThreadResult"+str(val): data})
import threading
def out_thread():
'''外层多线程'''
global OuterThreadResult
OuterThreadResult = []
threads = [] # 存放线程
l2=["1","2","3","4"]
for val in l2:
# 创建子线程并存放至列表threads
l = threading.Thread(target=outer_thread_function, args=(val,))
threads.append(l)
for t in threads:
# 开启线程
t.start()
for t in threads:
# 主线程等待子线程执行
t.join()
return {"data": OuterThreadResult}
OuterThreadResult = out_thread()
print(OuterThreadResult)
运行结果:
{‘data’: [{‘OuterThreadResult1’: {‘innerResult’: [‘in2’, ‘in1’]}}, {‘OuterThreadResult2’: {‘innerResult’: [‘in1’, ‘in2’]}}, {‘OuterThreadResult3’: {‘innerResult’: [‘in2’, ‘in1’, ‘in2’]}}, {‘OuterThreadResult4’: {‘innerResult’: [‘in2’, ‘in1’, ‘in2’]}}]}
从上述结果可以看出’OuterThreadResult3’,'OuterThreadResult4’结果不符合预期
结果分析:
这很明显是线程安全问题,(注意:如果是单层线程,一般要到达万的计算量级才会出现线程安全问题)。
既然是外层线程获取innerResult出现问题,考虑在外层线程加锁,这样每个外部线程都会有单独的innerResult=[]
import threading
lock = threading.Lock()
"""实现内层线程向列表里追加元素,外层线程调用内层线程,其中外层线程调用1次,内层线程会调用4次(4个内层线程)"""
def inner_thread_function(val):
'''
:param val:
:return: 向内层线程列表中(innerResult:全局变量)添加值
'''
innerResult.append(val)
def inner_thread():
"""内层线程"""
threads = [] # 存放线程
l1=["in1","in2"]
global innerResult
innerResult = []
for val in l1:
# 创建子线程并存放至列表threads
l = threading.Thread(target=inner_thread_function, args=(val,))
threads.append(l)
for t in threads:
# 开启线程
t.start()
for t in threads:
# 主线程等待子线程执行
t.join()
return {"innerResult": innerResult}
def outer_thread_function(val):
'''
在这里调用内层多线程
:param val:
:return:向外层线程列表(OuterThreadResult:全局变量列表)中调用内层线程,并添加内层线程返回值
'''
lock.acquire()
data = inner_thread()
OuterThreadResult.append({"OuterThreadResult"+str(val): data})
lock.release()
import threading
def out_thread():
'''外层多线程'''
global OuterThreadResult
OuterThreadResult = []
threads = [] # 存放线程
l2=["1","2","3","4"]
for val in l2:
# 创建子线程并存放至列表threads
l = threading.Thread(target=outer_thread_function, args=(val,))
threads.append(l)
for t in threads:
# 开启线程
t.start()
for t in threads:
# 主线程等待子线程执行
t.join()
return {"data": OuterThreadResult}
OuterThreadResult = out_thread()
print(OuterThreadResult)
返回值:
{‘data’: [{‘OuterThreadResult1’: {‘innerResult’: [‘in1’, ‘in2’]}}, {‘OuterThreadResult2’: {‘innerResult’: [‘in1’, ‘in2’]}}, {‘OuterThreadResult3’: {‘innerResult’: [‘in1’, ‘in2’]}}, {‘OuterThreadResult4’: {‘innerResult’: [‘in1’, ‘in2’]}}]}
注意,经试验发现,嵌套的线程,若不在外层线程加锁,极易发生线程安全问题
但这么加锁本质和单线程没有区别呀,每次都要等一个外部线程执行完了,才可以执行下一个外部线程,这不就是循化吗(其实,一般情况下,我们对于锁的位置是需要谨慎考量的,一般要加在影响全局变量的语句位置,这个程序加在哪里都不合适)
考虑方法三
既然问题是全局变量innerResult会出现问题,那不妨每次个内部线程都给一个独立的全局变量,外部进程要执行四次,那我们创建4个全局变量不就可以了,
import threading
"""实现内层线程向列表里追加元素,外层线程调用内层线程,其中外层线程调用1次,内层线程会调用4次(4个内层线程)"""
def inner_thread_function(val,idx):
'''
:param val:
:return: 向内层线程列表中(innerResult:全局变量)添加值
'''
globals()["innerResult_"+idx].append(val)
def inner_thread(idx):
"""内层线程"""
threads = [] # 存放线程
l1=["in1","in2"]
globals()["innerResult_"+idx] = [] #
for val in l1:
# 创建子线程并存放至列表threads
l = threading.Thread(target=inner_thread_function, args=(val,idx))
threads.append(l)
for t in threads:
# 开启线程
t.start()
for t in threads:
# 主线程等待子线程执行
t.join()
return {"innerResult": globals()["innerResult_"+idx]}
def outer_thread_function(val):
'''
在这里调用内层多线程
:param val:
:return:向外层线程列表(OuterThreadResult:全局变量列表)中调用内层线程,并添加内层线程返回值
'''
data = inner_thread(val)
OuterThreadResult.append({"OuterThreadResult"+str(val): data})
import threading
def out_thread():
'''外层多线程'''
global OuterThreadResult
OuterThreadResult = []
threads = [] # 存放线程
l2=["1","2","3","4"]
for val in l2:
# 创建子线程并存放至列表threads
l = threading.Thread(target=outer_thread_function, args=(val,))
threads.append(l)
for t in threads:
# 开启线程
t.start()
for t in threads:
# 主线程等待子线程执行
t.join()
return {"data": OuterThreadResult}
OuterThreadResult = out_thread()
print(OuterThreadResult)
{‘data’: [{‘OuterThreadResult1’: {‘innerResult’: [‘in1’, ‘in2’]}}, {‘OuterThreadResult2’: {‘innerResult’: [‘in1’, ‘in2’]}}, {‘OuterThreadResult3’: {‘innerResult’: [‘in1’, ‘in2’]}}, {‘OuterThreadResult4’: {‘innerResult’: [‘in1’, ‘in2’]}}]}
threading.Condition()进行有条件判断的锁
import threading
import time
import random
"生产者生产1-3号商品,消费者消费1-3号商品,如商品总数<1,消费者不可以消费,商品总数>10,生产者不可以生产,2个生产者,1个消费者"
cond = threading.Condition()
def customer_get_milk():
while True:
cond.acquire()
if len(milk_list)<1:
cond.wait() # 不满足条件,进入线程池等待
else:
print("消费者正在消费第s%号牛奶", milk_list[0])
milk_list.remove(milk_list[0])
cond.notify() # 执行完线程,唤醒线程池的其他线程
cond.release() # 释放锁
def productor_yield_milk(num):
while True:
cond.acquire()
if len(milk_list) > num:
cond.wait()
else:
flag= 0
for i in list(range(1,4)):
if i not in milk_list:
milk_list.append(i)
flag =1
print("生产者正在生产第s%号牛奶", i)
if not flag:
temp = random.randint(1,3)
milk_list.append(temp)
print("生产者正在生产第s%号牛奶", temp)
cond.notify()
cond.release()
def thread_main():
global milk_list
milk_list = []
t1 = threading.Thread(target=customer_get_milk, args=[])
t2 = threading.Thread(target=productor_yield_milk, args=(10,))
t3 = threading.Thread(target=productor_yield_milk, args=(10,))
t1.start()
t2.start()
t3.start()
t1.join()
t2.join()
t3.join()
if __name__ == '__main__':
thread_main()
参考:【python】详解threading模块:Condition类的使用(三)