.pop()
ผมสังเกตเห็นว่ามันก็มักจะแนะนำให้ใช้คิวที่มีหลายกระทู้แทนของรายการและ เป็นเพราะรายการไม่ปลอดภัยสำหรับเธรดหรือด้วยเหตุผลอื่น
.pop()
ผมสังเกตเห็นว่ามันก็มักจะแนะนำให้ใช้คิวที่มีหลายกระทู้แทนของรายการและ เป็นเพราะรายการไม่ปลอดภัยสำหรับเธรดหรือด้วยเหตุผลอื่น
คำตอบ:
รายการตัวเองปลอดภัยต่อเธรด ใน CPython GIL จะป้องกันการเข้าถึงพร้อมกันและการใช้งานอื่น ๆ ระมัดระวังที่จะใช้การล็อคแบบละเอียดหรือประเภทข้อมูลที่ซิงโครไนซ์สำหรับการใช้งานรายการ อย่างไรก็ตามในขณะที่รายการตัวเองไม่สามารถเสียหายได้โดยความพยายามในการเข้าถึงพร้อมกันข้อมูลของรายการจะไม่ได้รับการป้องกัน ตัวอย่างเช่น:
L[0] += 1
ไม่รับประกันว่าจะเพิ่ม L [0] ทีละคนถ้าเธรดอื่นทำสิ่งเดียวกันเพราะ+=
ไม่ใช่การดำเนินการปรมาณู (การดำเนินการที่น้อยมากใน Python เป็นอะตอมจริงเนื่องจากส่วนใหญ่สามารถทำให้รหัส 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
ที่ระดับหลามล่าม (คุณจะเห็นพวกเขาใน รวบรวม bytecode) append
จะทำใน AA เรียกวิธีการเดียวในโค้ดไบต์
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)
ส่งออกเมื่อข้อผิดพลาด
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
ข้อสรุป
ดังที่ได้กล่าวไว้ในคำตอบก่อนหน้านี้ในขณะที่การกระทำของการผนวกหรือ popping องค์ประกอบจากรายการตัวเองเป็นด้ายปลอดภัยสิ่งที่ไม่ปลอดภัยเธรดคือเมื่อคุณผนวกในหนึ่งกระทู้และป๊อปในอีก
with r:
) แทนการโทรอย่างชัดเจนr.acquire()
และr.release()