1. 程式人生 > 實用技巧 >deque - queue data structure of Python

deque - queue data structure of Python

deque

https://pymotw.com/2/collections/deque.html

A double-ended queue, or deque, supports adding and removing elements from either end. The more commonly used stacks and queues are degenerate forms of deques, where the inputs and outputs are restricted to a single end.

A deque can be populated from either end, termed “left” and “right” in the Python implementation.

import collections

# Add to the right
d = collections.deque()
d.extend('abcdefg')
print 'extend    :', d
d.append('h')
print 'append    :', d

# Add to the left
d = collections.deque()
d.extendleft('abcdefg')
print 'extendleft:', d
d.appendleft('h')
print 'appendleft:', d

deque是執行緒安全的嗎?

由於GIL特性,有些介面是安全的。

但是其不是用於執行緒間交換資料的。

https://www.zhihu.com/question/52605823

作者:tomsheep
連結:https://www.zhihu.com/question/52605823/answer/155754207
來源:知乎
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。

一、首先第一個問題, deque原始碼裡沒有用鎖保護critical section,是如何實現執行緒安全的?

看上去沒有鎖,但實際上是有的,沒錯就是GIL。

我們看Python文件中的解釋

the rule exists that only the thread that has acquired the GIL may operate on Python objects or call Python/C API functions. In order to emulate concurrency of execution, the interpreter regularly tries to switch threads (see sys.setcheckinterval()). The lock is also released around potentially blocking I/O operations like reading or writing a file, so that other Python threads can run in the meantime.

這段話的主要資訊有:

1. 只有獲取了GIL鎖的執行緒才能操作Python物件或呼叫Python/C API

2. 什麼時候會當前執行緒會釋放GIL呢?一個是python直譯器每執行若干條python指令(預設值100)後會嘗試做執行緒切換,讓其他執行緒有機會執行。另一個是進行IO操作時,會釋放掉GIL,當前執行緒進入睡眠。

那麼deque的C語言實現中,以pop為例(deque_append_internal這個函式內容就不貼了)

static PyObject *
deque_append(dequeobject *deque, PyObject *item)
{
    Py_INCREF(item);
    if (deque_append_internal(deque, item, deque->maxlen) < 0)
        return NULL;
    Py_RETURN_NONE;
}

在通過Python/C API呼叫這個函式時,當前執行緒肯定是先拿到了GIL的,而在這個函式內部,沒有主動釋放GIL(如何釋放,參考Initialization, Finalization, and Threads) 所以整個函式可以看作被GIL保護的critical section。

這裡正好也有一個相關的討論,Issue 15329: clarify which deque methods are thread-safe 可以拉到最後看Raymond的回答。

第二個問題,既然有GIL,那為啥在很多時候我們寫的程式碼還要加鎖?

因為——再重複一次上面提到的——python直譯器每執行若干條python指令(預設值100)後會嘗試做執行緒切換(釋放GIL)。注意跟上面用Python/C API實現的函式不同,C函式中如果沒有主動釋放GIL,是不會有機會切走的。

結論:

The deque's append(), appendleft(), pop(), popleft(), and len(d) operations are thread-safe in CPython

deque只能作為一種普通的佇列資料結構

https://stackoverflow.com/questions/717148/queue-queue-vs-collections-deque/20330499#20330499

沒有執行緒間通訊功能。

也不要將其在不同執行緒間共享,否則如同玩火。

Queue.Queue and collections.deque serve different purposes. Queue.Queue is intended for allowing different threads to communicate using queued messages/data, whereas collections.deque is simply intended as a datastructure. That's why Queue.Queue has methods like put_nowait(), get_nowait(), and join(), whereas collections.deque doesn't. Queue.Queue isn't intended to be used as a collection, which is why it lacks the likes of the in operator.

It boils down to this: if you have multiple threads and you want them to be able to communicate without the need for locks, you're looking for Queue.Queue; if you just want a queue or a double-ended queue as a datastructure, use collections.deque.

Finally, accessing and manipulating the internal deque of a Queue.Queue is playing with fire - you really don't want to be doing that.