使用线程锁定在FIFO队列中执行函数

时间:2018-04-22 21:14:28

标签: python python-multithreading

我想在FIFO队列中执行函数调用,并从每次调用中获取结果。

def func1(arg1):
    return arg1
def func2(arg1):
    return arg1

我会随机地从不同的线程调用这些函数。基本上我想要的是,一次只执行其中一个函数,我希望能够从每次调用中获取返回值。

2 个答案:

答案 0 :(得分:1)

尝试这样的事情:

import threading
import time


def func1(arg1):
    return arg1

def func2(arg1):
    return arg1

def square(x):
    time.sleep(5)
    return x * x

one_at_a_time = threading.Lock()
action_to_func_mapping = {
    "action1": func1,
    "action2": func2,
    "square_number": square,
}

def dispatch(action, arg1):
    one_at_a_time.acquire()
    func_to_call = action_to_func_mapping[action]
    func_result = func_to_call(arg1)
    one_at_a_time.release()
    return func_result

以下是一个例子:

if __name__ == '__main__':
    square_result = dispatch('square_number', 2)
    print(square_result)
    func1_result = dispatch('action1', 3)
    print(func1_result)


>>> python dispatch.py
4
3

你无法在这里看到锁的效果:示例是单线程的,线程之间永远不会有任何争用。您可以使用一些简单的线程代码更新问题,以显示锁定操作。

在多线程上下文中,上面的代码将阻塞其他线程并等待第一个线程完成其功能的执行。在您的应用程序中,您可以更有效地执行某些操作。例如,每个函数可能有一个锁,而不是所有函数调用的一个一个一个漏斗:

action_to_func_mapping = {
    "action1": func1,
    "action2": func2,
    "square_number": square,
}
action_to_func_with_perfunc_lock = {action: (func, threading.Lock())
     for action, func in action_to_func_mapping.items()}


def dispatch(action, arg1):
    func_to_call, func_lock = action_to_func_with_perfunc_lock[action]
    func_lock.acquire()
    func_result = func_to_call(arg1)
    func_lock.release()
    return func_result

>>> python dispatch.py
4
3

这次square_number将阻止下一个square_number,直到第一个action1完成,但action2square_number未被Teeth <- caries$after - caries$before anova <- aov(Teeth~inst * treat, data=caries) summary(anova) ggplot(data = caries, aes(x=inst, y=Teeth, group=inst)) + geom_boxplot(colour = "black", fill = "dodgerblue") 电话阻止。

此处使用了一些Python工具:dict comprehensionstuples,元组解包。

答案 1 :(得分:0)

这是一个包含FIFO队列和线程的完整示例,对比每个功能锁和一次一个锁。前面的答案是正确的,但不能证明锁定策略及其效果,因为那里的代码是单线程的。本回答介绍了线程,以便确实发生线程锁定,您可以看到结果。

import threading
from collections import deque
from time import sleep, time


def duck(behaviour):
    sleep(1)
    return behaviour


def sloth(behaviour):
    sleep(5)
    return behaviour


def swift(behaviour):
    sleep(0.1)
    return behaviour


animal_to_func = {
    "sloth": sloth,
    "duck": duck,
    "swift": swift,
}
one_at_a_time_lock = threading.Lock()
animal_to_func_with_per_animal_lock = {action: (func, threading.Lock())
                                       for action, func in animal_to_func.items()}

fifo = deque()


class AnimalThread(threading.Thread):
    def __init__(self, thread_id, use_per_animal_lock=False):
        threading.Thread.__init__(self)
        self.thread_id = thread_id
        self.use_per_animal_lock = use_per_animal_lock

    def run(self):
        while len(fifo):
            animal, behaviour = fifo.popleft()
            animal_action = dispatch(animal, behaviour, self.use_per_animal_lock)
            print('  (thread %s) %s: %s' % (self.thread_id, animal, str(animal_action)))


def dispatch(animal, behaviour, use_per_animal_lock=False):
    if use_per_animal_lock:
        animal_function, lock = animal_to_func_with_per_animal_lock[animal]
    else:
        lock = one_at_a_time_lock
        animal_function = animal_to_func[animal]
    lock.acquire()
    what_just_happened = animal_function(behaviour)
    lock.release()
    return what_just_happened


if __name__ == '__main__':
    monday_morning = [
        ('sloth', 'wake up'),
        ('sloth', 'blink'),
        ('sloth', 'go back to sleep'),
        ('duck', 'wake up'),
        ('duck', 'quack'),
        ('duck', 'waddle'),
        ('duck', 'swim'),
        ('duck', 'fight duck #2'),
        ('duck', 'quack'),
        ('swift', 'wake up'),
        ('swift', 'catch insects'),
        ('swift', 'feed chicks'),
    ]
    # essentially unlimited threads to force locks to be used
    no_of_threads = len(monday_morning)
    print('One at a time, no pushing and shoving!...')
    # load the FIFO queue the threads will consume
    fifo.extend(monday_morning)
    # start threads
    threads = [AnimalThread(i) for i in range(no_of_threads)]
    [thread.start() for thread in threads]
    # wait for threads to finish
    [thread.join() for thread in threads]
    print('One of each kind of animal at a time...')
    # load the FIFO queue the threads will consume
    fifo.extend(monday_morning)
    # start threads
    threads = [AnimalThread(threadno, use_per_animal_lock=True) for threadno in range(no_of_threads)]
    [thread.start() for thread in threads]
    # wait for threads to finish
    [thread.join() for thread in threads]

这是一个例子:

 One at a time, no pushing and shoving!...
   (thread 0) sloth: wake up
   (thread 1) sloth: blink
   (thread 2) sloth: go back to sleep
   (thread 3) duck: wake up
   (thread 4) duck: quack
   (thread 5) duck: waddle
   (thread 6) duck: swim
   (thread 7) duck: fight duck #2
   (thread 8) duck: quack
   (thread 9) swift: wake up
   (thread 10) swift: catch insects
   (thread 11) swift: feed chicks
 One of each kind of animal at a time...
   (thread 9) swift: wake up
   (thread 10) swift: catch insects
   (thread 11) swift: feed chicks
   (thread 3) duck: wake up
   (thread 4) duck: quack
   (thread 5) duck: waddle
   (thread 6) duck: swim
   (thread 0) sloth: wake up
   (thread 7) duck: fight duck #2
   (thread 8) duck: quack
   (thread 1) sloth: blink
   (thread 2) sloth: go back to sleep

一次一个锁定遵循FIFO队列中项目的顺序。它是一个只允许一次调用一个函数的漏斗。它的问题在于它有效地将多线程应用程序转换为串行的单线程应用程序。

当上述代码中出现每个动物的锁定时,动物一次只能做一件事(树懒不能同时醒来并眨眼)但是它们独立于彼此可以继续自己的生活。鸭子和懒惰之前的快速结束甚至醒来。但是因为鸭子忙碌的早晨,一次只能做一件事,所以树懒在鸭子完成之前就会醒来。

这种锁定在时间敏感的应用程序中很有用,在这些应用程序中,某些类型的函数需要按照它们到达队列的顺序进行处理。货币交易就是一个例子,你可能有几个与一个账户相关的工作单元,这些单位必须按照它们到达队列的顺序进行处理。

相关问题