मैं ध्यान देता हूं कि अक्सर सूचियों के बजाय और कई थ्रेड्स के साथ कतारों का उपयोग करने का सुझाव दिया जाता है .pop()
। क्या यह इसलिए है क्योंकि सूचियाँ थ्रेड-सुरक्षित नहीं हैं, या किसी अन्य कारण से?
मैं ध्यान देता हूं कि अक्सर सूचियों के बजाय और कई थ्रेड्स के साथ कतारों का उपयोग करने का सुझाव दिया जाता है .pop()
। क्या यह इसलिए है क्योंकि सूचियाँ थ्रेड-सुरक्षित नहीं हैं, या किसी अन्य कारण से?
जवाबों:
सूचियाँ स्वयं थ्रेड-सुरक्षित हैं। CPython में GIL उनके लिए समवर्ती अभिगम से बचाता है, और अन्य कार्यान्वयन उनकी सूची कार्यान्वयन के लिए एक महीन दाने वाले लॉक या एक सिंक्रनाइज़ डेटाटाइप का उपयोग करने के लिए ध्यान रखते हैं। हालाँकि, जबकि सूचियाँ स्वयं समवर्ती पहुँच के प्रयासों से भ्रष्ट नहीं हो सकती हैं, सूचियों का डेटा संरक्षित नहीं है। उदाहरण के लिए:
L[0] += 1
वास्तव में एल [0] को बढ़ाने की गारंटी नहीं है यदि एक और धागा एक ही काम करता है, क्योंकि +=
एक परमाणु ऑपरेशन नहीं है। (बहुत, पायथन में बहुत कम संचालन वास्तव में परमाणु हैं, क्योंकि उनमें से अधिकांश को मनमाने ढंग से पायथन कोड कहा जा सकता है।) आपको क्युस का उपयोग करना चाहिए क्योंकि यदि आप सिर्फ एक असुरक्षित सूची का उपयोग करते हैं, तो आप दौड़ के कारण गलत तरीके से प्राप्त कर सकते हैं या हटा सकते हैं। शर्तेँ।
थॉमस के उत्कृष्ट जवाब में एक बिंदु को स्पष्ट करने के लिए, यह उल्लेखनीय है कि 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
अजगर दुभाषिया के स्तर पर (आप उन्हें में देखेंगे संकलित bytecode)। append
बाईटकोड में आ ही विधि कॉल में किया जाता है।
remove
?
यहाँ उदाहरण के लिए एक व्यापक अभी तक संक्षिप्त सूची है की list
संचालन और चाहे या नहीं वे धागा सुरक्षित हैं। यहांobj in a_list
भाषा निर्माण के संबंध में उत्तर पाने की आशा है ।
मेरे पास हाल ही में यह मामला था, जहाँ मुझे एक सूची में लगातार एक थ्रेड में संलग्न करने की आवश्यकता थी, आइटम के माध्यम से लूप और जाँच करें कि क्या आइटम तैयार था, यह मेरे मामले में एक AsyncResult था और इसे सूची से केवल तभी हटा दें जब यह तैयार था। मुझे कोई भी ऐसा उदाहरण नहीं मिला जिससे मेरी समस्या स्पष्ट रूप से प्रदर्शित हो। एक उदाहरण है कि एक थ्रेड में लगातार सूची में जोड़ने और दूसरे थ्रेड में एक ही सूची से लगातार निकालने का प्रदर्शन किया जाता है। त्रुटिपूर्ण संस्करण छोटी संख्याओं पर आसानी से चलता है, लेकिन संख्याओं को बड़ा रखें और एक रन बनाएं कुछ समय और आप त्रुटि देखेंगे
फ्लावेड संस्करण
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()