[python] 목록은 스레드로부터 안전합니까?
목록과 대신에 여러 스레드가있는 대기열을 사용하는 것이 좋습니다 .pop()
. 목록이 스레드로부터 안전하지 않거나 다른 이유로 인해 발생합니까?
답변
목록 자체는 스레드로부터 안전합니다. CPython에서 GIL은 동시 액세스에 대해 보호하고 다른 구현에서는 목록 구현에 세밀한 잠금 또는 동기화 된 데이터 유형을 사용하도록주의를 기울입니다. 그러나 동시에 액세스하려는 시도로 인해 목록 자체 가 손상 될 수는 없지만 목록의 데이터 는 보호되지 않습니다. 예를 들면 다음과 같습니다.
L[0] += 1
+=
원자 연산이 아니기 때문에 다른 스레드가 동일한 작업을 수행하는 경우 실제로 L [0]을 1 씩 증가시킬 수 는 없습니다. (실제로 파이썬에서는 거의 임의의 작업이 원자 적입니다. 대부분의 작업은 임의의 Python 코드를 호출 할 수 있기 때문입니다.) 보호되지 않은 목록을 사용하는 경우 경쟁으로 인해 잘못된 항목을 가져 오거나 삭제할 수 있으므로 큐를 사용해야합니다. 정황.
답변
토마스의 우수한 대답 점을 명확히하기 위해, 그 언급해야 append()
입니다 스레드 안전합니다.
데이터 를 쓰려고 하면 읽은 데이터가 같은 위치에있을 염려가 없기 때문입니다 . 작업은 단지 목록에 데이터를 기록 데이터를 읽지 않습니다.append()
답변
다음 은 list
작업 예제 와 스레드 안전 여부에 대한 포괄적이지만 포괄적이지 않은 목록입니다 . obj in a_list
언어 구성 에 대한 답변을 얻으려면 여기를 클릭하십시오 .
답변
나는 최근에 하나의 스레드에서 목록에 지속적으로 추가하고, 항목을 반복하고 항목이 준비되었는지 확인하고, 내 경우에는 AsyncResult 였고, 준비된 경우에만 목록에서 제거 해야하는이 경우가있었습니다. 내 문제를 명확하게 보여주는 예를 찾을 수 없습니다. 다음은 한 스레드의 목록에 지속적으로 추가하고 다른 스레드의 동일한 목록에서 지속적으로 제거하는 방법을 보여주는 예제입니다. 결함이있는 버전은 작은 숫자에서 쉽게 실행되지만 숫자는 충분히 크게 유지하고 몇 번 오류가 표시됩니다
FLAWED 버전
import threading
import time
# Change this number as you please, bigger numbers will get the error quickly
count = 1000
l = []
def add():
for i in range(count):
l.append(i)
time.sleep(0.0001)
def remove():
for i in range(count):
l.remove(i)
time.sleep(0.0001)
t1 = threading.Thread(target=add)
t2 = threading.Thread(target=remove)
t1.start()
t2.start()
t1.join()
t2.join()
print(l)
ERROR시 출력
Exception in thread Thread-63:
Traceback (most recent call last):
File "/Users/zup/.pyenv/versions/3.6.8/lib/python3.6/threading.py", line 916, in _bootstrap_inner
self.run()
File "/Users/zup/.pyenv/versions/3.6.8/lib/python3.6/threading.py", line 864, in run
self._target(*self._args, **self._kwargs)
File "<ipython-input-30-ecfbac1c776f>", line 13, in remove
l.remove(i)
ValueError: list.remove(x): x not in list
잠금을 사용하는 버전
import threading
import time
count = 1000
l = []
lock = threading.RLock()
def add():
with lock:
for i in range(count):
l.append(i)
time.sleep(0.0001)
def remove():
with lock:
for i in range(count):
l.remove(i)
time.sleep(0.0001)
t1 = threading.Thread(target=add)
t2 = threading.Thread(target=remove)
t1.start()
t2.start()
t1.join()
t2.join()
print(l)
산출
[] # Empty list
결론
이전 답변에서 언급했듯이 목록 자체에서 요소를 추가하거나 팝하는 작업은 스레드로부터 안전하지만 스레드 안전하지 않은 것은 한 스레드를 추가하고 다른 스레드를 팝할 때입니다