簡體   English   中英

限制Python線程的並發和速率

[英]Limiting concurrency and rate for Python threads

給定多個線程,我想將對worker函數的調用速率限制為每秒1個的速率。

我的想法是跟蹤所有線程上次調用的時間,並將其與每個線程中的當前時間進行比較。 然后,如果current_time - last_time < rate 我讓線程睡了一會兒。 我的實現存在問題-我想我可能對鎖的工作方式有錯誤的認識。

我的代碼:

from Queue import Queue
from threading import Thread, Lock, RLock
import time

num_worker_threads = 2
rate = 1
q = Queue()
lock = Lock()
last_time = [time.time()]

def do_work(i, idx):
    # Do work here, print is just a dummy.
    print('Thread: {0}, Item: {1}, Time: {2}'.format(i, idx, time.time()))

def worker(i):
    while True:
        lock.acquire()
        current_time = time.time()
        interval = current_time - last_time[0]
        last_time[0] = current_time
        if interval < rate:
            time.sleep(rate - interval)
        lock.release()
        item = q.get()
        do_work(i, item)
        q.task_done()

for i in range(num_worker_threads):
     t = Thread(target=worker, args=[i])
     t.daemon = True
     t.start()

for item in xrange(10):
    q.put(item)

q.join()

我原本希望每秒看到一次do_work調用,但是,我同時do_work收到2個調用(每個線程1個),然后暫停一秒鍾。 怎么了?


好的,進行一些編輯。 只是簡單地限制將物品放入隊列的速度的建議是好的,但是我記得我不得不照顧工人將物品重新添加到隊列中的情況。 典型示例:網絡任務中的分頁或退避重試。 我提出了以下內容。 我猜想對於實際的網絡任務,eventlet / gevent庫在資源上可能會更容易,但這只是一個例子。 它基本上使用優先級隊列來堆積請求,並使用額外的線程以均勻的速率將項目從堆中鏟除到實際任務隊列中。 我模擬了工人將其重新插入樁中的情況,然后首先對重新插入的物品進行處理。

import sys
import os
import time
import random

from Queue import Queue, PriorityQueue
from threading import Thread

rate = 0.1

def worker(q, q_pile, idx):
    while True:
        item = q.get()
        print("Thread: {0} processed: {1}".format(item[1], idx))
        if random.random() > 0.3:
            print("Thread: {1} reinserting item: {0}".format(item[1], idx))
            q_pile.put((-1 * time.time(), item[1]))
        q.task_done()

def schedule(q_pile, q):
    while True:
        if not q_pile.empty():
            print("Items on pile: {0}".format(q_pile.qsize()))
            q.put(q_pile.get())
            q_pile.task_done()
        time.sleep(rate)

def main():

    q_pile = PriorityQueue()
    q = Queue()

    for i in range(5):
        t = Thread(target=worker, args=[q, q_pile, i])
        t.daemon = True
        t.start()

    t_schedule = Thread(target=schedule, args=[q_pile, q])
    t_schedule.daemon = True
    t_schedule.start()

    [q_pile.put((-1 * time.time(), i)) for i in range(10)]
    q_pile.join()
    q.join()

if __name__ == '__main__':
    main()

嘗試限制多個線程之間的速率對我來說似乎很奇怪。 如果您單獨限制每個線程,則可以避免所有的鎖定問題。

只是一種猜測,但我想你想設置last_time[0]time.time()current_time后) sleep

我幾乎同時接到2個電話(每個線程1個),然后停一秒鍾。 怎么了?

這正是您應該從實現中獲得的期望。 假設時間t從0開始,比率為1:

Thread1這樣做:

    lock.acquire() # both threads wait here, one gets the lock
    current_time = time.time() # we start at t=0
    interval = current_time - last_time[0] # so interval = 0
    last_time[0] = current_time # last_time = t = 0
    if interval < rate: # rate = 1 so we sleep
        time.sleep(rate - interval) # to t=1
    lock.release() # now the other thread wakes up
    # it's t=1 and we do the job

Thread2這樣做:

    lock.acquire() # we get the lock at t=1 
    current_time = time.time() # still t=1
    interval = current_time - last_time[0] # interval = 1
    last_time[0] = current_time
    if interval < rate: # interval = rate = 1 so we don't sleep
        time.sleep(rate - interval)
    lock.release() 
    # both threads start the work around t=1

我的建議是限制將物品放入隊列的速度

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM