Queue.Queue বনাম সংগ্রহ। ডেক


181

আমার এমন একটি সারি দরকার যা একাধিক থ্রেডগুলিতে স্টাফ রাখতে পারে এবং একাধিক থ্রেড পড়তে পারে।

পাইথনের কমপক্ষে দুটি সারি ক্লাস রয়েছে, কুই.উইউ এবং সংগ্রহগুলি.ডেক, প্রাক্তনটি আপাতদৃষ্টিতে অভ্যন্তরীণভাবে ব্যবহার করে। উভয়ই ডকুমেন্টেশনে থ্রেড-নিরাপদ বলে দাবি করে।

যাইহোক, সারি ডক্সে আরও বলা হয়েছে:

কালেকশন.ডেক হ'ল দ্রুত পারমাণবিক সংযোজন () এবং পোপফেল্ট () অপারেশন সহ আনবাউন্ডেড সারিগুলির বিকল্প বাস্তবায়ন যা লকিংয়ের প্রয়োজন হয় না।

যা আমি অনুমান করি যে আমি বেশ আনসারস্ট্যান্ড নই: এর অর্থ কি deque পুরোপুরি থ্রেড-নিরাপদ নয়?

যদি তা হয় তবে আমি দুটি শ্রেণীর মধ্যে পার্থক্যটি পুরোপুরি বুঝতে পারি না। আমি দেখতে পাচ্ছি যে সারি ব্লকিং কার্যকারিতা যুক্ত করে। অন্যদিকে, এটি ইন-অপারেটরটির জন্য সমর্থন মতো কিছু দর্শনীয় বৈশিষ্ট্য হারিয়েছে।

অভ্যন্তরীণ deque অবজেক্টটি সরাসরি অ্যাক্সেস করা হয়

x কাতারে ()। deque

থ্রেড নিরাপদ?

এছাড়াও, যখন ডিউটি ​​থ্রেড-নিরাপদ ইতিমধ্যে সুরক্ষিত রয়েছে তখন কেন কুইউ তার ক্রিয়াকলাপগুলির জন্য একটি মিটেক্স নিয়োগ করে?


RuntimeError: deque mutated during iterationআপনি যা পেতে পারেন তা dequeবেশ কয়েকটি থ্রেড এবং লকিংয়ের মধ্যে ভাগ করে নেওয়া ব্যবহার করা হচ্ছে ...
টয়াইন

4
@ টোইনগুলির থ্রেডগুলির সাথে যদিও কিছু করার নেই। আপনি যখনই dequeএকই থ্রেডে পুনরাবৃত্তি করতে গিয়ে কিছুক্ষণ যোগ / মুছুন তখনই আপনি এই ত্রুটিটি পেতে পারেন । আপনি এই ত্রুটিটিটি থেকে পেতে না পারার একমাত্র কারণ Queueএটি Queueপুনরাবৃত্তিকে সমর্থন করে না।
সর্বোচ্চ

উত্তর:


280

Queue.Queueএবং collections.dequeবিভিন্ন উদ্দেশ্যে পরিবেশন। Queue.Queue বিভিন্ন থ্রেডকে সারিবদ্ধ বার্তা / ডেটা ব্যবহার করে যোগাযোগ করার অনুমতি দেওয়ার জন্য তৈরি করা হয়েছে, যেখানে collections.dequeকেবল ডেটাস্ট্রাকচার হিসাবেই লক্ষ্য করা হচ্ছে। যে কেন Queue.Queueমত পদ্ধতি আছে put_nowait(), get_nowait()এবং join()যেহেতু, collections.dequeনা। Queue.Queueসংগ্রহ হিসাবে ব্যবহার করার উদ্দেশ্যে নয়, তাই এটি inঅপারেটরের পছন্দগুলির অভাব বোধ করে ।

এটি এতে উত্‍সাহিত হয়: যদি আপনার একাধিক থ্রেড থাকে এবং আপনি যদি চান যে তালাবলীর প্রয়োজন ছাড়াই যোগাযোগ করতে সক্ষম হন তবে আপনি সন্ধান করছেন Queue.Queue; আপনি যদি ডেটাস্ট্রাকচার হিসাবে কেবল একটি সারি বা ডাবল-এন্ড সারি চান তবে ব্যবহার করুন collections.deque

শেষ অবধি, কোনওটির অভ্যন্তরীণ ডিকের অ্যাক্সেস এবং হেরফেরটি Queue.Queueআগুনের সাথে খেলছে - আপনি সত্যই এটি করতে চান না।


6
না, এটি মোটেই ভাল ধারণা নয়। আপনি যদি উত্সটি দেখেন তবে এটি হুডের নীচে Queue.Queueব্যবহার dequeকরে। collections.dequeএটি একটি সংগ্রহ, যখন Queue.Queueএকটি যোগাযোগ ব্যবস্থা। ওভারহেড ইন Queue.Queueথ্রেডসেফ করা হয়। dequeথ্রেডগুলির মধ্যে যোগাযোগের জন্য ব্যবহার করা কেবল বেদনাদায়ক ঘোড়দৌড়ের দিকে নিয়ে যায়। dequeথ্রেডসেফ হতে যখনই ঘটে যায়, দোভাষী কীভাবে প্রয়োগ করা হয় তার একটি আনন্দের দুর্ঘটনা, এবং নির্ভরযোগ্য কিছু নয় । এজন্যই Queue.Queueপ্রথম স্থানে রয়েছে।
কিথ গোগান

2
কেবল মনে রাখবেন যে আপনি যদি থ্রেডগুলি জুড়ে যোগাযোগ করছেন, আপনি ডেকে ব্যবহার করে আগুন নিয়ে খেলছেন। জিআইএল- এর অস্তিত্বের কারণে দুর্ঘটনাক্রমে ডেক থ্রেডসেফ হয় । একটি জিআইএল-কম প্রয়োগের ক্ষেত্রে পুরোপুরি আলাদা পারফরম্যান্সের বৈশিষ্ট্য থাকবে, সুতরাং অন্যান্য বাস্তবায়নের ছাড়টি বুদ্ধিমানের কাজ নয়। তদুপরি, আপনি কি একক থ্রেডে এর নিষ্প্রভ মাপদণ্ডের ব্যবহারের বিপরীতে থ্রেডগুলি জুড়ে ব্যবহারের জন্য ক্যু বনাম দ্বিগুণ সময়সীমা করেছেন? আপনার কোড থাকে তবে যে সারি গতি deque বনাম সংবেদনশীল, পাইথন ভাষা আপনি যা খুঁজছেন নাও হতে পারে।
কিথ গোগান

3
@ কিথগৌহান deque is threadsafe by accident due to the existence of GIL; এটি সত্য যে dequeথ্রেড-সুরক্ষা নিশ্চিত করতে জিআইএল-র উপর নির্ভর করে - তবে তা নয় by accident। অফিসিয়াল অজগর ডকুমেন্টেশন পরিষ্কারভাবে বলেছে যে deque pop*/ append*পদ্ধতিগুলি থ্রেড-নিরাপদ। সুতরাং কোনও বৈধ অজগর বাস্তবায়ন অবশ্যই একই গ্যারান্টি সরবরাহ করতে হবে (জিআইএল-কম বাস্তবায়নগুলি জিআইএল ছাড়া এটি কীভাবে করতে হবে তা নির্ধারণ করতে হবে)। আপনি সেই গ্যারান্টিগুলিতে নিরাপদে নির্ভর করতে পারেন।
সর্বাধিক

2
@ পূর্ববর্তী মন্তব্যগুলি সত্ত্বেও, আপনি কীভাবে dequeযোগাযোগের জন্য ব্যবহার করবেন তা আমি পুরোপুরি বুঝতে পারি না । আপনি মোড়ানো যদি popএকটি মধ্যে try/except, আপনি একটি ব্যস্ত লুপ CPU- র বিরাট পরিমাণ খাওয়া শুধু নতুন তথ্যের অপেক্ষায় দিয়ে শেষ হবে। এটি প্রদত্ত ব্লকিং কলগুলির তুলনায় ভয়াবহভাবে অকার্যকর পদ্ধতির মতো বলে মনে হচ্ছে Queue, যা নিশ্চিত করে যে ডেটার জন্য অপেক্ষা করা থ্রেডটি ঘুমিয়ে যাবে এবং সিপিইউ সময় নষ্ট করবে না।
সর্বাধিক

3
আপনি সেই সময়ের জন্য সোর্স কোডটি একটি রিড নিতে চাইতে পারেন Queue.Queue, কারণ এটি ব্যবহার করে লেখা হয়েছে collections.deque: hg.python.org/cpython/file/2.7/Lib/Queue.py - এটি কন্ডিশনে ভেরিয়েবলগুলি কার্যকরভাবে এর মোড়কেdeque অ্যাক্সেস করার অনুমতি দেয় সুরক্ষিত এবং দক্ষতার সাথে থ্রেডের সীমানা ছাড়িয়ে। আপনি কীভাবে dequeযোগাযোগের জন্য একটি ব্যবহার করবেন তার ব্যাখ্যা ঠিক উত্সটিতে রয়েছে।
কিথ গোগান

44

যদি আপনি যা কিছু সন্ধান করছেন তা থ্রেডের মধ্যে অবজেক্টগুলি স্থানান্তর করার একটি থ্রেড-নিরাপদ উপায় তবে উভয়ই কাজ করবে (উভয়ই ফিফো এবং লিফোর জন্য)। ফিফোর জন্য:

বিঃদ্রঃ:

  • অন্যান্য অপারেশনগুলি dequeথ্রেড নিরাপদ নাও হতে পারে, আমি নিশ্চিত নই।
  • dequeব্লক করে না pop()বা popleft()তাই কোনও নতুন আইটেম না আসা পর্যন্ত আপনি আপনার গ্রাহক থ্রেড প্রবাহকে অবরুদ্ধ করতে পারবেন না।

যাইহোক, এটি দেখে মনে হয় যে ডেকের একটি কার্যকর দক্ষতার সুবিধা রয়েছে । 100 কে আইটেম সন্নিবেশ করানো এবং অপসারণের জন্য সিপিথন ২.7.৩ ব্যবহার করে সেকেন্ডে কিছু মানদণ্ডের ফলাফল এখানে রয়েছে

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

1
আপনি দাবি করেছেন যে "" অন্যান্য অপারেশনগুলি dequeথ্রেড নিরাপদ নাও হতে পারে "। আপনি কোথায় থেকে যে পেতে পারি?
ম্যাট

@Matt - ভাল আমার অর্থ বহন করা পুনরায় তৈরি
জনাথন

3
ঠিক আছে ধন্যবাদ. এটি আমাকে যোগ্য ব্যবহার করতে বাধা দিচ্ছিল কারণ আমি ভেবেছিলাম আপনি এমন কিছু জানেন যা আমি করি নি। আমি অনুমান করি যে আমি অন্যথায় আবিষ্কার না করা অবধি আমি এটির থ্রেডটি নিরাপদ বলে ধরে নেব।
ম্যাট

@ ম্যাট "ডিকি'র অ্যাপেনড (), অ্যাপেন্ডেলফ্ট (), পপ (), পপলফট () এবং লেন (ডি) ক্রিয়াকলাপগুলি সিপিথনে থ্রেড-নিরাপদ।" উত্স: bugs.python.org/issue15329
ফিলিপ্পো

7

তথ্যের জন্য ডাইক থ্রেড-সুরক্ষা ( https://bugs.python.org/issue15329 ) জন্য একটি পাইথন টিকিট রেফারেন্স করা আছে । শিরোনাম "কোন উপযুক্ত পদ্ধতিগুলি থ্রেড-নিরাপদ তা স্পষ্ট করুন"

নীচের লাইনটি এখানে: https://bugs.python.org/issue15329#msg199368

ডিকি'র অ্যাপেনড (), অ্যাপেন্ডেলফ্ট (), পপ (), পোপফেল্ট () এবং লেন (ডি) ক্রিয়াকলাপগুলি সিপাইথনে থ্রেড-নিরাপদ। সংযোজন পদ্ধতিগুলির শেষে একটি ডিসেকআরএফ রয়েছে (ম্যাক্সেলেন সেট করা হয়েছে এমন ক্ষেত্রে) তবে কাঠামোগুলির সমস্ত আপডেট হয়ে যাওয়ার পরে এবং আক্রমণকারীদের পুনরুদ্ধার করার পরে এটি ঘটে, সুতরাং এই ক্রিয়াকলাপগুলিকে পারমাণবিক হিসাবে বিবেচনা করা ঠিক হবে।

যাইহোক, আপনি যদি 100% নিশ্চিত না হন এবং আপনি পারফরম্যান্সের তুলনায় নির্ভরযোগ্যতা পছন্দ করেন তবে কেবল একটি লক রাখুন;)


6

এতে থাকা সমস্ত একক-উপাদান পদ্ধতিগুলি dequeপারমাণবিক এবং থ্রেড-নিরাপদ। অন্যান্য সমস্ত পদ্ধতি থ্রেড-নিরাপদ। ভীষণ পছন্দ len(dq), dq[4]ক্ষণস্থায়ী সঠিক মান ফলন হয়। তবে মনে করুন উদাহরণস্বরূপ dq.extend(mylist): আপনি কোনও গ্যারান্টি পাবেন না যে mylistঅন্য থ্রেডগুলিও একই পক্ষের উপাদানগুলিকে সংযোজন করে যখন সমস্ত সারিতে একটি সারিতে ফাইল করা হয় - তবে এটি সাধারণত আন্ত-থ্রেড যোগাযোগের জন্য এবং প্রশ্নবিদ্ধ কার্যের প্রয়োজন হয় না।

সুতরাং এর dequeচেয়ে 20 ডলার দ্রুত Queue(যা dequeহুডের নীচে ব্যবহার করে) এবং আপনার যদি "আরামদায়ক" সিঙ্ক্রোনাইজেশন এপিআই (ব্লকিং / টাইমআউট) প্রয়োজন না হয় তবে কঠোর maxsizeআনুগত্য বা "এই পদ্ধতিগুলিকে ওভাররাইড করুন (_পুট, _গেট, ..) ) অন্যান্য কিউ সংস্থাগুলি " উপ-শ্রেণিবদ্ধ আচরণ, বা আপনি নিজে যখন এই ধরণের জিনিসগুলি যত্ন নেওয়ার জন্য বাস্তবায়ন করেন , তখন dequeউচ্চ গতির আন্তঃ-থ্রেড যোগাযোগের জন্য একটি খালি একটি ভাল এবং দক্ষ চুক্তি।

প্রকৃতপক্ষে অতিরিক্ত মুউটেক্স এবং অতিরিক্ত পদ্ধতি ._get()ইত্যাদি পদ্ধতির ভারী ব্যবহারের কারণটি Queue.pyপিছনের দিকের সামঞ্জস্যতার সীমাবদ্ধতা, অতীতের ওভার ডিজাইন এবং আন্ত-থ্রেড যোগাযোগের ক্ষেত্রে এই গুরুত্বপূর্ণ গতির বাধা সমস্যাটির কার্যকর সমাধান দেওয়ার জন্য যত্নের অভাবের কারণ। পুরানো পাইথন সংস্করণে একটি তালিকা ব্যবহৃত হয়েছিল - তবে এমনকি list.append () /। পপ (0) ছিল পারমাণবিক এবং থ্রেডসেফ ...


3

notify_all()প্রত্যেকটিতে যুক্ত করা deque appendএবং ডিফল্ট আচরণের দ্বারা অর্জন করা 20x উন্নতির চেয়ে popleftআরও খারাপ ফলাফলের ফলাফল :dequedeque

deque + notify_all: 0.469802
Queue:              0.667279

@ জোনাথন তার কোডটি কিছুটা সংশোধন করুন এবং আমি সিপাইথন ৩..2.২ ব্যবহার করে বেঞ্চমার্কটি পেয়েছি এবং আচরণ কুইয়ের অনুকরণের জন্য ডেক লুপের শর্ত যুক্ত করছি।

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()

কালেকশন.ডেক হ'ল দ্রুত পারমাণবিক সংযোজন () এবং পোপফেল্ট () অপারেশন সহ আনবাউন্ডেড সারিগুলির বিকল্প বাস্তবায়ন যা লকিংয়ের প্রয়োজন হয় না। ডকস ক্যু


2

dequeথ্রেড-নিরাপদ। "ক্রিয়াকলাপগুলির জন্য যেগুলি লকিংয়ের প্রয়োজন হয় না" তার অর্থ আপনার নিজের কাছে লকিংয়ের দরকার নেই, এটি সেটিকে দেখায় deque

কটাক্ষপাত গ্রহণ Queueউৎস, অভ্যন্তরীণ deque বলা হয় self.queueএবং accessors এবং পরিব্যক্তি জন্য mutex ব্যবহার করে, তাই Queue().queueহয় না থ্রেড-নিরাপদ ব্যবহারের।

আপনি যদি কোনও "ইন" অপারেটর খুঁজছেন, তবে কোনও সমস্যা বা সারি সম্ভবত আপনার সমস্যার জন্য সবচেয়ে উপযুক্ত ডেটা কাঠামো নয়।


1
ঠিক আছে, আমি কি করতে চাই তা নিশ্চিত করে নিন যে কপির সাথে কোনও সদৃশ যুক্ত হবে না। এটি কি এমন কিছু নয় যা একটি সারির সম্ভাব্য সমর্থন করতে পারে?
चमत्कार 2k

1
পৃথক সেট থাকা সম্ভবত সবচেয়ে ভাল হবে এবং আপনি যখন সারি থেকে কোনও কিছু যুক্ত / সরান তখন আপডেট করুন। এটি ও (এন) এর পরিবর্তে ও (লগ এন) হবে, তবে আপনাকে সেট এবং সারিটি সিঙ্কে রাখার জন্য সতর্কতা অবলম্বন করতে হবে (অর্থাত লক করা)।
ব্রায়ান-ব্রাজিল

পাইথন সেট হ্যাশ টেবিল, তাই এটি ও (1) হবে। তবে হ্যাঁ, আপনাকে এখনও লকিং করতে হবে।
আফোগলিয়া

1

(মনে হয় মন্তব্য করার মতো খ্যাতি আমার নেই ...) আপনার বিভিন্ন থ্রেড থেকে ডেকির কোন পদ্ধতি ব্যবহার করা উচিত সে সম্পর্কে আপনাকে সতর্ক হওয়া উচিত।

deque.get () থ্রেডসেফ হিসাবে উপস্থিত বলে মনে হচ্ছে, তবে আমি এটি করতে পেরেছি

for item in a_deque:
   process(item)

যদি অন্য থ্রেড একই সাথে আইটেম যুক্ত করে তবে ব্যর্থ হতে পারে। আমি একটি রানটাইম এক্সেপশন পেয়েছি যা অভিযোগ করেছে "পুনরাবৃত্তির সময় ডিউক মিউটেশন"।

এর দ্বারা কোন ক্রিয়াকলাপগুলি প্রভাবিত হয় তা দেখতে সংগ্রহশালা মডুল.এল পরীক্ষা করুন


থ্রেড এবং প্রধান থ্রেড-সুরক্ষার জন্য এই জাতীয় ত্রুটি বিশেষ নয়। এটি ঘটে যেমন স্রেফ >>> di = {1:None} >>> for x in di: del di[x]
করণ

1
মূলত আপনার অন্য থ্রেড দ্বারা সংশোধিত হতে পারে এমন কোনও জিনিসটি কখনই লুপ করা উচিত নয় (যদিও কিছু ক্ষেত্রে আপনি নিজের সুরক্ষা যোগ করে এটি করতে পারেন)। একটি সারিটির মতো, আপনি কোনও আইটেমটি প্রক্রিয়া করার আগে পপ / পাতাগুলি থেকে বের করার উদ্দেশ্যে নিয়েছিলেন এবং আপনি এটি সাধারণত একটি whileলুপ দিয়ে করেন।
চমত্কার
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.