多处理中的有序状态打印

时间:2018-03-11 17:31:35

标签: python multithreading multiprocessing locking gil

我有一个程序,我必须在我的程序执行的每个成功步骤后打印给用户,我尝试使用Lock但它确实减慢了我的程序。

基本上我想做的是每次成功操作打印到用户(有序),就像我有一些代码执行发布到某些页面并打印X已按顺序执行的操作

显示我想要做的事情的一个例子:(它似乎有效)但它确实减慢了任务:

lock = Lock()

def run(u):
    lock.acquire()
    buff = []
    e = requests.post('#personal_url', data=mydata)
    buff.append(e.text)
    buff.append('----------------')
    f = requests.get('#personal_urlx')
    buff.append(u + ' --> ' f.text)
    print('\n'.join(buff))
    lock.release()

p = Pool(4)
p.map(run, uu)
p.close()
p.join()

非常感谢任何帮助,谢谢。

2 个答案:

答案 0 :(得分:1)

你的程序可能会减慢你的锁定策略。锁仅应用于保护代码的所谓关键部分,这些部分包含共享资源,如果未正确保护,则可能包含无效状态。

所以我的建议是,如果您唯一关心的是在stdout上有有效的输出(意味着您的打印件没有被打断并且打印出整行),请尝试通过编写扩展打印功能来保护您的标准输出并使用您的只锁在那里。像这样:

Viewed

从您当前的代码中,请删除锁上的操作,并仅在Viewed函数内使用锁定。

def ext_print(str, lock):
    lock.acquire()
    print(str)
    lock.release()

使用这种方法,您应该在标准输出上获得干净的输出。请注意,使用此方法可以将输出写入stdout,延迟意味着具有两个线程ext_printdef run(u): buff = [] e = requests.post('#personal_url', data=mydata) buff.append(e.text) buff.append('----------------') f = requests.get('#personal_urlx') buff.append(u + ' --> ' f.text) ext_print('\n'.join(buff), lock) ,您可能获得t1的输出在t2的输出之前开始的时间晚于t2,即使t1已完成t1之前的数据处理。因此,这种方法将提高多线程所实现的性能和性能增益,但并不能保证输出反映完成的t1t2操作的完全相同的顺序。

我认为以与每个完成的操作相同的顺序编写输出的唯一方法是使用这样的解决方案:

get

你可以猜到这个的表现可能会更糟。

答案 1 :(得分:0)

更新

我在阅读您的评论后对代码进行了一些更改。可能不是一个很好的方法,但基本上,我派出另一个进程,将在某个时间间隔轮询共享字典并更新控制台输出。请注意,这将在更新时清除整个控制台。希望这是预期的行为。

代码:

from multiprocessing import Lock, Process, Pool, Manager
import time

def run(user,logs):
    logs[user] += ['Message 1 for user: ' + user]
    time.sleep(2) #some work done per user
    logs[user] += ['Message 2 for user: ' + user]
    return 1

manager = Manager()
logs = manager.dict()

users = ['Tom', 'Bob', 'Dinesh', 'Ravi']

for user in users:
    logs[user] = [] #initialize empty list for each user

logs_list = [logs for i in range(len(users))] 

def poll(logs):
    while True:
        print("\033c") #clear the console
        for user in logs.keys():
            print('Logs for user:', user)
            print('\n'.join(logs[user]))
            print('----------------')
        time.sleep(0.1)

poller_process = Process(target=poll, args=(logs,))

poller_process.start()
p = Pool(4)
p.starmap(run, zip(users,logs_list))
p.close()
p.join()
poller_process.join()

------
Output #logs under each user are refreshed constantly
------
Logs for user: Tom
Message 1 for user: Tom
Message 2 for user: Tom
----------------
Logs for user: Bob
Message 1 for user: Bob
Message 2 for user: Bob
----------------
Logs for user: Dinesh
Message 1 for user: Dinesh
Message 2 for user: Dinesh
----------------
Logs for user: Ravi
Message 1 for user: Ravi
Message 2 for user: Ravi
----------------

这可能不是一种非常优雅的方法,但它有效。您可以尝试将“每个流程”的结果与“用户”进行汇总。密钥在共享字典中。然后,您可以在pool.join()之后遍历字典并按顺序打印所有结果。这将消除对锁的需求。

代码看起来像这样:

from multiprocessing import Lock, Process, Pool, Manager
import time

def run(user,logs):
    logs[user] += ['Message 1 for user: ' + user]
    time.sleep(1) #some work done per user
    logs[user] += ['Message 2 for user: ' + user]
    return 1

manager = Manager()
logs = manager.dict()

users = ['Tom', 'Bob', 'Dinesh', 'Ravi']

for user in users:
    logs[user] = [] #initialize empty list for each user

logs_list = [logs for i in range(len(users))] 

p = Pool(4)
p.starmap(run, zip(users,logs_list))
p.close()
p.join()

for user in logs.keys():
    print(logs[user])


------
Output:  
------
['Message 1 for user: Tom', 'Message 2 for user: Tom']
['Message 1 for user: Bob', 'Message 2 for user: Bob']
['Message 1 for user: Dinesh', 'Message 2 for user: Dinesh']
['Message 1 for user: Ravi', 'Message 2 for user: Ravi']