답변:
목록 자체는 스레드로부터 안전합니다. CPython에서 GIL은 동시 액세스에 대해 보호하고 다른 구현에서는 목록 구현에 세밀한 잠금 또는 동기화 된 데이터 유형을 사용하도록주의를 기울입니다. 그러나 동시에 액세스하려는 시도로 인해 목록 자체 가 손상 될 수는 없지만 목록의 데이터 는 보호되지 않습니다. 예를 들면 다음과 같습니다.
L[0] += 1
+=
원자 연산이 아니기 때문에 다른 스레드가 동일한 작업을 수행하는 경우 실제로 L [0]을 1 씩 증가시킬 수 는 없습니다. (실제로 파이썬에서는 거의 임의의 작업이 원자 적입니다. 대부분의 작업은 임의의 Python 코드를 호출 할 수 있기 때문입니다.) 보호되지 않은 목록을 사용하는 경우 경쟁으로 인해 잘못된 항목을 가져 오거나 삭제할 수 있으므로 큐를 사용해야합니다. 정황.
토마스의 우수한 대답 점을 명확히하기 위해, 그 언급해야 append()
입니다 스레드 안전합니다.
데이터 를 쓰려고 하면 읽은 데이터가 같은 위치에있을 염려가 없기 때문입니다 . 작업은 단지 목록에 데이터를 기록 데이터를 읽지 않습니다.append()
PyList_Append
은 하나의 GIL 잠금으로 수행됩니다. 추가 할 객체에 대한 참조가 제공됩니다. 해당 객체의 내용은 평가 된 후 및 호출 PyList_Append
이 완료 되기 전에 변경 될 수 있습니다 . 하지만 여전히 동일한 객체, 안전하게 (당신이 경우에 추가 될 것입니다 lst.append(x); ok = lst[-1] is x
, 다음 ok
물론, 거짓 일 수 있음). 참조 코드는 INCREF 코드를 제외하고 추가 된 객체에서 읽지 않습니다. 추가 된 목록을 읽고 재 할당 할 수 있습니다.
L[0] += x
을 수행 __getitem__
에 L
다음 __setitem__
에 L
경우 - L
지원 __iadd__
은 객체 인터페이스에서 다르게 조금을 할 것입니다,하지만 두 개의 별도의 작업이 여전히있다 L
파이썬 인터프리터 수준에서 (당신이 그들을 볼 수 있습니다 컴파일 된 바이트 코드). 는 append
바이트 코드에서 AA 하나의 메소드 호출로 이루어집니다.
remove
?
다음 은 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
결론
이전 답변에서 언급했듯이 목록 자체에서 요소를 추가하거나 팝하는 작업은 스레드로부터 안전하지만 스레드 안전하지 않은 것은 한 스레드를 추가하고 다른 스레드를 팝할 때입니다
with r:
) 대신 명시 적으로 호출 r.acquire()
하고r.release()