[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

결론

이전 답변에서 언급했듯이 목록 자체에서 요소를 추가하거나 팝하는 작업은 스레드로부터 안전하지만 스레드 안전하지 않은 것은 한 스레드를 추가하고 다른 스레드를 팝할 때입니다


답변