여러 스레드가 물건을 넣을 수 있고 여러 스레드가 읽을 수있는 대기열이 필요합니다.
파이썬에는 적어도 두 개의 큐 클래스 인 Queue.Queue와 collections.deque가 있으며, 전자는 후자를 내부적으로 사용합니다. 둘 다 설명서에서 스레드 안전하다고 주장합니다.
그러나 큐 문서에는 다음과 같은 상태가 있습니다.
collections.deque는 잠금이 필요없는 빠른 원자 append () 및 popleft () 작업을 사용하여 언 바운드 큐의 대체 구현입니다.
내가 이해하지 못하는 것 : 이것이 deque가 완전히 스레드 안전하지 않다는 것을 의미합니까?
그렇다면 두 클래스의 차이점을 완전히 이해하지 못할 수도 있습니다. 대기열에 차단 기능이 추가되어 있음을 알 수 있습니다. 반면에 작동 자에 대한 지원과 같은 일부 deque 기능이 손실됩니다.
내부 deque 객체에 직접 액세스하는 것은
Queue ()에서 x
스레드 안전?
또한 큐가 이미 스레드 안전 상태 일 때 큐가 뮤텍스를 사용하여 작동하는 이유는 무엇입니까?
답변
Queue.Queue
및 collections.deque
다른 목적을 제공합니다. Queue.Queue는 다른 스레드가 대기중인 메시지 / 데이터를 사용하여 통신 할 수 있도록 collections.deque
하는 것이 아니라 단순히 데이터 구조로 사용하기위한 것입니다. 그의 왜 Queue.Queue
같은 방법을 가지고 put_nowait()
, get_nowait()
하고 join()
있는 반면, collections.deque
그렇지 않습니다. Queue.Queue
컬렉션으로 사용하도록 의도되지 않았으므로 in
연산자 와 같은 기능이 부족합니다 .
여러 스레드가 있고 잠금없이 통신 할 수있게하려면 다음을 찾으십시오 Queue.Queue
. 큐 또는 이중 엔드 큐를 데이터 구조로 사용하려면을 사용하십시오 collections.deque
.
마지막으로, 내부의 deque에 접근하고 조작하는 Queue.Queue
것은 불을 피우고 있습니다-당신은 정말로 그렇게하고 싶지 않습니다.
답변
찾고있는 모든 것이 스레드간에 객체를 전송하는 스레드 안전 방법 이라면 둘 다 작동합니다 (FIFO 및 LIFO 모두). FIFO의 경우 :
노트 :
- 에 대한 다른 작업은
deque
스레드 안전하지 않을 수 있습니다. deque
에 차단하지 않습니다pop()
또는popleft()
새 항목이 도착할 때까지 차단에 소비자 스레드 흐름을 기반으로 할 수 있도록.
그러나 deque는 상당한 효율성 이점이있는 것으로 보입니다 . 다음은 100k 항목 삽입 및 제거에 CPython 2.7.3을 사용하는 몇 초 만에 벤치 마크 결과입니다.
deque 0.0747888759791
Queue 1.60079066852
벤치 마크 코드는 다음과 같습니다.
import time
import Queue
import collections
q = collections.deque()
t0 = time.clock()
for i in xrange(100000):
q.append(1)
for i in xrange(100000):
q.popleft()
print 'deque', time.clock() - t0
q = Queue.Queue(200000)
t0 = time.clock()
for i in xrange(100000):
q.put(1)
for i in xrange(100000):
q.get()
print 'Queue', time.clock() - t0
답변
자세한 정보는 deque thread-safety ( https://bugs.python.org/issue15329 )에 대한 Python 티켓이 있습니다. 제목 “스레드에 안전한 deque 메소드를 명시하십시오”
결론은 다음과 같습니다. https://bugs.python.org/issue15329#msg199368
deque의 append (), appendleft (), pop (), popleft () 및 len (d) 연산은 CPython에서 스레드 안전합니다. 추가 메소드에는 끝에 maxCRES가 설정된 경우 DECREF가 있지만 모든 구조 업데이트가 수행되고 불변이 복원 된 후에 발생하므로 이러한 조작을 원자로 처리해도됩니다.
어쨌든 100 % 확실하지 않고 성능보다 신뢰성을 선호하는 경우 잠금 장치처럼 넣으십시오.)
답변
모든 단일 요소 메소드 deque
는 원자적이고 스레드로부터 안전합니다. 다른 모든 방법은 스레드로부터 안전합니다. 상황이 좋아 len(dq)
, dq[4]
순간 정확한 값을 얻을 수 있습니다. 그러나 예를 들어 생각해보십시오 dq.extend(mylist)
: mylist
다른 스레드가 같은면에 요소를 추가 할 때 모든 요소 가 연속적으로 줄 지어 있다는 보장을 얻지 못하지만 일반적으로 스레드 간 통신 및 문제가있는 작업의 요구 사항은 아닙니다.
따라서 a deque
는 ~보다 20 배 빠릅니다 Queue
( deque
언더 후드 를 사용함 ). “편안한”동기화 API (차단 / 시간 초과), 엄격한 maxsize
준수 또는 “이 메소드 무시 (_put, _get, .. ) 다른 큐 조직의 서브 클래 싱 동작 을 구현 하거나 직접 deque
처리하는 경우 고속 스레드 간 통신에 적합하고 효율적입니다.
실제로 여분의 뮤텍스와 여분의 메소드 ._get()
등의 메소드 호출 이 과도하게 사용되는 것은 Queue.py
이전의 호환성 제한, 과거의 과도한 설계 및 스레드 간 통신 에서이 중요한 속도 병목 현상 문제에 대한 효율적인 솔루션을 제공하기위한주의가 부족하기 때문입니다. 목록은 이전 Python 버전에서 사용되었지만 list.append () /. pop (0)조차도 원자적이고 스레드 안전합니다 …
답변
추가 notify_all()
각 deque
append
과 popleft
에 대한 훨씬 더 나쁜 결과에 결과 deque
보다 20 배 개선은 기본적으로 달성 deque
행동 :
deque + notify_all: 0.469802
Queue: 0.667279
@Jonathan은 코드를 약간 수정하고 cPython 3.6.2를 사용하여 벤치 마크를 얻고 큐에서 동작을 시뮬레이션하기 위해 deque 루프에 조건을 추가합니다.
import time
from queue import Queue
import threading
import collections
mutex = threading.Lock()
condition = threading.Condition(mutex)
q = collections.deque()
t0 = time.clock()
for i in range(100000):
with condition:
q.append(1)
condition.notify_all()
for _ in range(100000):
with condition:
q.popleft()
condition.notify_all()
print('deque', time.clock() - t0)
q = Queue(200000)
t0 = time.clock()
for _ in range(100000):
q.put(1)
for _ in range(100000):
q.get()
print('Queue', time.clock() - t0)
그리고이 기능에 의해 성능이 제한되는 것 같습니다 condition.notify_all()
collections.deque는 잠금이 필요없는 빠른 원자 append () 및 popleft () 작업을 사용하여 언 바운드 큐의 대체 구현입니다.
docs Queue
답변
deque
스레드 안전합니다. “잠금이 필요없는 작업”은 잠금을 직접 수행 할 필요가 없다는 것을 의미 deque
합니다.
상기보고 촬영 Queue
소스를 내부 양단 큐가 호출됩니다 self.queue
그렇게하고, 접근 및 돌연변이에 대한 뮤텍스를 사용 Queue().queue
한다 하지 스레드 안전 용도에 관한 것이다.
“in”연산자를 찾고 있다면 deque 또는 queue가 문제에 가장 적합한 데이터 구조가 아닐 수도 있습니다.
답변
(나는 언급 할 평판이 없다고 생각합니다 …) 다른 스레드에서 사용하는 deque의 방법을주의해야합니다.
deque.get ()은 스레드로부터 안전한 것으로 보이지만
for item in a_deque:
process(item)
다른 스레드가 동시에 항목을 추가하는 경우 실패 할 수 있습니다. “반복 중에 deque mutate”라는 불평을하는 RuntimeException이 발생했습니다.
collectionsmodule.c 를 점검 하여 이로 인해 영향을받는 작업을 확인하십시오.