文章目录

  • 循环调用多线程
  • 生产者消费者问题(threading.Condition())


循环调用多线程

希望实现下面效果
{‘data’: [{‘OuterThreadResult1’: {‘innerResult’: [‘in1’, ‘in2’]}}, {‘OuterThreadResult2’: {‘innerResult’: [‘in1’, ‘in2’]}}, {‘OuterThreadResult3’: {‘innerResult’: [‘in1’, ‘in2’]}}, {‘OuterThreadResult4’: {‘innerResult’: [‘in1’, ‘in2’]}}]}

  • 内层多线程是向列表 innerResult = []添加元素,
  • 内层多线程调用N次,并每次将内层返回的结果innerResult作为值,‘OuterThreadResulti’(i=1,…,N)作为键,并将键值对结果追加到OuterThreadResult = []中

最简单的方法自然是内层多线程写完后,外层直接循环调用内层多线程,但这种方法会拖慢程序运行速度,这里不做赘述。

第二种方法是外层多线程也考虑用多线程实现,起名外层多线程:

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’结果不符合预期

结果分析:

  • 外部线程1 进入内部线程后,外部线程1的内部线程1会首先获取到全局变量innerResult = [],
    并添加了l1的元素"in1",innerResult = [“in1”],
  • 此时外部线程2也进入内部线程,外部线程2 的内部线程1会首先获取到全局变量innerResult = [],并添加元素"in1",innerResult = [“in1”];
  • 此时可能外部线程2 的内部线程2也抢到了线程操控权,它又向全局变量添加了l1的元素"in2",innerResult = [“in1”,“in2”];
  • 然后外部线程1 的内部线程2会首先获取到全局变量innerResult = [“in1”,“in2”] – 这里已经不是我们希望的结果了,出现了全局变量修改混乱的问题,并添加了l1的元素"in2",innerResult = [“in1”,“in2” ,“in2”]

这很明显是线程安全问题,(注意:如果是单层线程,一般要到达万的计算量级才会出现线程安全问题)。
既然是外层线程获取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())

threading.Condition()进行有条件判断的锁

  • 问题描述:“生产者生产1-3号商品,消费者消费1-3号商品,如商品总数<1,消费者不可以消费,商品总数>10,生产者不可以生产,2个生产者,1个消费者”
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类的使用(三)