Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/arrays/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 为什么此数组值被删除但仍打印?_Python_Arrays_Multithreading_Queue - Fatal编程技术网

Python 为什么此数组值被删除但仍打印?

Python 为什么此数组值被删除但仍打印?,python,arrays,multithreading,queue,Python,Arrays,Multithreading,Queue,这个脚本是我拥有的一个更大的脚本的一个非常简单的版本,我在这里做的事情是删除“pip”,如果有这样的数组值。但问题是当第二个周期到来时(甚至更多),pip继续在屏幕上打印,我不想这样做 import requests, threading, random, string, json, time, queue, re num_worker_threads = int(input("Threads: ")) lista = ['asd', 'asdjk', 'pip', 'lasd', 'lol

这个脚本是我拥有的一个更大的脚本的一个非常简单的版本,我在这里做的事情是删除“pip”,如果有这样的数组值。但问题是当第二个周期到来时(甚至更多),pip继续在屏幕上打印,我不想这样做

import requests, threading, random, string, json, time, queue, re

num_worker_threads = int(input("Threads: "))

lista = ['asd', 'asdjk', 'pip', 'lasd', 'lol']
print(str(lista))

def do_work(i):
    try:
        print(i.strip())
        print(str(lista))
        if i == "pip":
            lista.remove("pip")
    except Exception as e:
        print(e)


def worker():
    while True:
        item = q.get()
        if item is not None:
            do_work(item)
            q.task_done()


q = queue.Queue()

threads = []

for i in range(num_worker_threads):
    t = threading.Thread(target=worker)
    t.start()
    threads.append(t)

for i in range(2): # I have only put 2 cycles
    for line in lista:
        q.put(line)


q.join()

for i in range(num_worker_threads):
    q.put(None)

for t in threads:
    t.join()

您的代码具有竞争条件。比赛分为两个阶段:

  • 主线程将尝试向队列添加两次
    “pip”
    ,每次在
    lista
    上迭代一次
  • 工作线程,它们共同从队列中提取项目并对其进行处理。如果他们处理一个
    “pip”
    条目,他们会将其从
    列表中删除
如果第一个
“pip”
条目在第二个
“pip”
被迭代并添加之前由工作线程处理并从
列表中删除,则不会再次处理它。但是,从您对问题的描述来看,我猜由于GIL和代码不同部分的计时的某种组合,您可以在工作人员可以做任何事情之前,将
“pip”
的两个副本都添加到队列中。但这不是严格保证。如果在主线程中的
lista
迭代之间添加了延迟,那么工作线程可能会有时间赶上并删除
“pip”
,正如您所期望的那样:

for i in range(2):
    for line in lista:
        q.put(line)
    time.sleep(1) # delay for a second
显然,这可能不是你真正想要做的,因为这仍然是一场比赛,只是一场可能被其他参赛者赢得的比赛(不能保证其他赛道也不会花太长时间)。更好的解决方案是设计代码,这样就根本没有竞争条件

一个想法可能是在列表的迭代之间加入队列,这样所有的值都会在重新添加之前由工作人员处理。这与
time.sleep
方法非常相似,但更可靠,因为主线程将等待工作人员处理队列中的项目所需的时间,然后再添加更多项目


另一个想法可能是在将值放入队列之前过滤主线程中的
“pip”
条目。只是一个
if行!=“pip”
check可能会这样做

您需要在多线程代码中进行可靠的并发控制,您需要确保以下几件事情按顺序发生:

  • 您希望确保队列不会再次读取列表,直到线程从插入队列的第一轮中清除了
    pip

  • 您需要确保线程不会同时变异相同的元素,这将导致其中一个线程抛出异常,即它无法删除已删除的元素

您可以使用
Event
对多线程程序的流进行一些控制,让我们声明一个名为
first\u iteration\u processed
的事件,队列将等待该事件得到满足,以便开始对列表的所有元素进行第二次迭代。 一旦从列表中成功删除pip,事件将由您的一个线程设置

代码示例:

import requests, threading, random, string, json, time, queue, re
from threading import Event
num_worker_threads = int(input("Threads: "))

lista = ['asd', 'asdjk', 'pip', 'lasd', 'lol']
print(str(lista))

iteration_processsed = Event()

iteration_processsed.set()
def do_work(i):
    # global lista
    try:
        print(i.strip())
        print(str(lista))
        if i == "pip":
            lista.remove("pip")
            print("Removed pip successfully")
        if i == "iter_end":
            iteration_processsed.set()
    except Exception as e:
        print(e)


def worker():
    while True:
        item = q.get()
        if item is not None:
            do_work(item)
            q.task_done()


q = queue.Queue()

threads = []

for i in range(num_worker_threads):
    t = threading.Thread(target=worker)
    t.start()
    threads.append(t)

for i in range(3): # I have only put 2 cycles
    iteration_processsed.wait()
    print(f"Iteration {i} started")
    iteration_processsed.clear()

    for line in lista:
        q.put(line)
    q.put('iter_end')

q.join()

for i in range(num_worker_threads):
    q.put(None)

for t in threads:
    t.join()
让我们试试这个:

Threads: 2
['asd', 'asdjk', 'pip', 'lasd', 'lol']
Iteration 0 started
asd
['asd', 'asdjk', 'pip', 'lasd', 'lol']
asdjk
['asd', 'asdjk', 'pip', 'lasd', 'lol']
pip
['asd', 'asdjk', 'pip', 'lasd', 'lol']
Removed pip successfully
lasd
['asd', 'asdjk', 'lasd', 'lol']
lol
iter_end
['asd', 'asdjk', 'lasd', 'lol']
['asd', 'asdjk', 'lasd', 'lol']
Iteration 1 started
asd
asdjk
['asd', 'asdjk', 'lasd', 'lol']
['asd', 'asdjk', 'lasd', 'lol']
lasd
lol
['asd', 'asdjk', 'lasd', 'lol']
['asd', 'asdjk', 'lasd', 'lol']
iter_end
['asd', 'asdjk', 'lasd', 'lol']
Iteration 2 started
asd
asdjk
['asd', 'asdjk', 'lasd', 'lol']
['asd', 'asdjk', 'lasd', 'lol']
lasd
lol
['asd', 'asdjk', 'lasd', 'lol']
['asd', 'asdjk', 'lasd', 'lol']
iter_end
['asd', 'asdjk', 'lasd', 'lol']

现在,正如您所看到的,在删除pip之前,第二次迭代将永远不会开始,当然,这里的实现非常特定于这种情况,但我想您可以根据自己更一般的目的对其进行调整,并可能添加更多事件以锁定更多操作,以按照预定义的顺序执行。 您可以从文档中阅读更多关于事件的信息,或者本文也是一个良好的开端

谢谢你的回答Kareem,我想问:如果我想让它成为一个永久的循环,而不是:“因为我在范围内(2):“这是一个”而不是“真实的:”?当然,检查我更新的答案。您需要将事件锁
迭代\u处理
以在所有后续迭代中使用。有效,但如果在第二次迭代中没有“pip”,则会出现错误。尝试使用相同的代码进行5个循环。然后,你的线程需要知道关于迭代意味着什么的更具体的信息,可能在完成队列归档的每个迭代后放置一个已知的关键字,如
q.put('iter_end')
,这样如果你的任何线程达到了该已知关键字,他释放锁,而不是在删除
pip
时释放锁。你能解释一下我如何使它循环无限次吗?这可能是一次迭代,它删除了“pip”,可能是它没有删除任何项目,也可能是另一个随机项目。问题是,我这样做是为了简化我的大脚本,但“pip”可能会在任何其他时刻更改为另一个值,所以我需要每个周期重新加载数组。