簡體   English   中英

多消費者單生產者隊列

[英]Multiple Consumer Single Producer Queue

我試圖找出Python中的同步問題。 我有一個生產者線程和(可選)多個使用者線程(取決於命令,即./script sums.txt -c 10)。 現在有了1個Producer和1個Consumer,就沒有問題了,因為同步是通過Queue處理的。

現在的問題是,如果有1個Consumers線程以上,則線程1可能會從隊列的線程中獲取一項並對其進行處理。 盡管線程2的執行速度相同,但比線程1的執行速度更快,並且在線程1之前執行打印。我嘗試使用隨機計時器來模擬此問題。

我的輸出現在帶有隨機計時器:“ ./script sommen.txt -c 2”正如您注意到隊列中的第二個項目在第一個項目之前處理,沒有隨機計時器不會發生很多事情,因為操作非常簡單所以線程足夠快。 有辦法解決這個問題嗎? 我想到了鎖,但是那會使程序效率低下?

另一件事,什么是清理線程的最佳方法。 我知道隊列的完成時間(前哨值),但是清理線程的好方法是什么?

非常感謝!

Consumers is set to: 2
I'm thread number: 4316991488 Read  (P): 12 + 90
I'm thread number: 4316991488 Read  (P): 420 / 20
I'm thread number: 4316991488 Read  (P): 12 + 90
I'm thread number: 4316991488 Read  (P): 420 / 20
Monitor is done
I'm thread number: 4329586688 Write (C): 420 / 20 = 21.0
I'm thread number: 4324331520 Write (C): 12 + 90 = 102

-

#!/usr/bin/env python

import threading
import operator
import sys
import queue
import optparse
from time import sleep
import random

def optionsparser():
    parser = optparse.OptionParser(
        usage="usage: %prog file [Options]")
    parser.add_option("-c", "--consumer", dest="consumer", type="int",
                      help="consumer <ident> [default: %default]")

    parser.set_defaults(consumer=1)
    opts, files = parser.parse_args()

    filename = files[0]

    try:
        _f = open(filename)
        return(filename, opts.consumer)
    except IOError:
        print ('Oh dear I/O Error')

def readitems(filename):

    print("Read from file: ", filename)
    with open(filename, 'r') as f:
        mylist = [line.rstrip('\n') for line in f]
    f.close()

    try: 
        for _line in mylist:
            data = _line.split(' ')

            qprint.put(data) #write to monitor queue
            qsum.put(data) #write to consumer queue

    except ValueError as e:
        print(e)
    except RuntimeError as err:
        print(err)
    finally:
        qsum.put("Done Flag")
        qprint.put("Done Flag")
def consumer(qsum):

    while qsum:
        sleeptime = random.randint(1,10)
        sleep(sleeptime)
        try:
            if qsum.get() == "Done Flag":
                print("Monitor queue empty", threading.get_ident())
                ## Clean up
                # Put bakc for other consumers
                qsum.put("Done Flag")
                #cleanup here

            else:
                data = qsum.get()
                operator = calc(data)

        except EnvironmentError as Err:
            print(Err)

def calc(data):

    try:
        sleeptime = random.randint(1,10)
        sleep(sleeptime)
        getal1, diff, getal2 = data
        getal1 = int(getal1)
        getal2 = int(getal2)

        if diff == '+':
            print("I'm thread number:", threading.get_ident(), "Write (C):", str(getal1), diff, str(getal2), "=",  operator.add(getal1, getal2))
        elif diff == '-':
            print("I'm thread number:", threading.get_ident(), "Write (C):", str(getal1), diff, str(getal2), "=",  operator.sub(getal1, getal2))
        elif diff == '*':
            print("I'm thread number:", threading.get_ident(), "Write (C):", str(getal1), diff, str(getal2), "=",  operator.mul(getal1, getal2))
        elif diff == '/':
            print("I'm thread number:", threading.get_ident(), "Write (C):", str(getal1), diff, str(getal2), "=",  operator.truediv(getal1, getal2))
        elif diff == '%':
            print("I'm thread number:", threading.get_ident(), "Write (C):", str(getal1), diff, str(getal2), "=",  operator.mod(getal1, getal2))
        elif diff == '**':
            print("I'm thread number:", threading.get_ident(), "Write (C):", str(getal1), diff, str(getal2), "=",  operator.pow(getal1, getal2))
        else:
            print("I'm thread number:", threading.get_ident(), "Write (C):", str(getal1), diff, str(getal2), "=", "Unknown operator!")

    except ZeroDivisionError as Err:
        print(Err)
    except ValueError:
        print("Wrong input")

def producer(reqs):  
    try:
        readitems(reqs)
    except IndexError as e:
        print(e)


def monitor(qprint):

    while qprint:
        try:
            if qprint.get() == "Done Flag":

                print("Monitor is done")
            else:
                data = (qprint.get())
                getal1, diff, getal2 = data
                print("I'm thread number:", threading.get_ident(), "Read  (P):", str(getal1), diff, str(getal2))
        except RuntimeError as e:
            print(e)

if __name__ == '__main__':

    try:
        reqs = optionsparser() 
        #create queu's
        qprint = queue.Queue()
        qsum = queue.Queue()
        #monitor threads
        t2 = threading.Thread(target=monitor, args=(qprint,))
        t2.start()
        #create consumers threads 
        thread_count = reqs[1]
        print("Consumers is set to:", thread_count)
        for i in range(thread_count):
            t = threading.Thread(target=consumer, args=(qsum,))
            t.start()

        #start producer 
        producer(reqs[0])

    except RuntimeError as Err:
        print(Err)
    except AssertionError as e:
        print(e)

當任務可以獨立分配和威脅時,使用線程非常有效。 如果要使用thead,請記住,當代碼中沒有鎖點或鎖點很少時,並行化代碼會更有效。 鎖定點可以是共享資源。

在您的情況下,您只是生成/使用數據,並且希望對其進行同步。 如果依次運行此代碼,效率會更高,否則,您將必須更准確地定義可以從並行化中受益的任務。

第一:不要使用Python線程來加速CPU限制的任務,例如計算。 除了減速外,您什么都看不到。 因為GIL 請勿將Python線程用於I / O綁定任務,例如URL提取。

如果希望結果按發布順序到達,請給每個隊列元素一個序列號。 這樣,每個任務都會知道其結果在哪里。

使用有序集合(例如列表),以序列號作為索引來放入工作線程產生的結果。 由於可能會以相反的順序接收結果,因此您需要將它們全部存儲(無法流式傳輸)。

我不明白為什么在這里使用鎖定。 首先,鎖通過阻止原本獨立的工作程序而無法達到並行處理的目的。 其次,鎖很難,而且容易產生細微的錯誤。 隊列更友好。

暫無
暫無

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

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