মাল্টিপ্রসেসিং পুলের মতো থ্রেডিং পুল?


347

মাল্টিপ্রসেসিং মডিউলটির পুল বর্গের মতো শ্রমিক থ্রেডের জন্য কি কোনও পুল শ্রেণি রয়েছে ?

আমি উদাহরণস্বরূপ মানচিত্রের ফাংশনটির সমান্তরাল করার সহজ উপায়টি পছন্দ করি

def long_running_func(p):
    c_func_no_gil(p)

p = multiprocessing.Pool(4)
xs = p.map(long_running_func, range(100))

তবে আমি নতুন প্রক্রিয়া তৈরির ওভারহেড ছাড়াই এটি করতে চাই।

আমি জিআইএল সম্পর্কে জানি যাইহোক, আমার ব্যবহারের ক্ষেত্রে, ফাংশনটি আইও-বাউন্ড সি ফাংশন হবে যার জন্য পাইথন মোড়কের আসল ফাংশন কলের আগে জিআইএল প্রকাশ করবে।

আমার নিজের থ্রেডিং পুলটি কি লিখতে হবে?


পাইথন কুকবুকটিতে এখানে এমন কিছু আশাবাদী মনে হচ্ছে: রেসিপি 576519: (বহু) প্রসেসিংয়ের মতো একই এপিআই সহ থ্রেড পুল। পুল (পাইথন)
অন্যচির্প

1
আজকাল বিল্ট-ইন আছে: from multiprocessing.pool import ThreadPool
মার্টিনো

আপনি এই সম্পর্কে বিস্তারিত বলতে পারেন I know about the GIL. However, in my usecase, the function will be an IO-bound C function for which the python wrapper will release the GIL before the actual function call.?
mrgloom

উত্তর:


448

আমি এইমাত্র খুজে পেলাম যে আসলে হয় একটি থ্রেড ভিত্তিক পুল ইন্টারফেস multiprocessingমডিউল তবে এটা কিছুটা লুকানো হয় এবং সঠিকভাবে নথিভুক্ত না।

এটি দিয়ে আমদানি করা যায়

from multiprocessing.pool import ThreadPool

এটি একটি অজগর থ্রেড মোড়ানো একটি ডামি প্রসেস ক্লাস ব্যবহার করে প্রয়োগ করা হয়। এই থ্রেড-ভিত্তিক প্রক্রিয়া শ্রেণীর সন্ধান পাওয়া যাবে multiprocessing.dummyযা দস্তাবেজে সংক্ষেপে উল্লেখ করা হয়েছে । এই ডামি মডিউলটি থ্রেডের উপর ভিত্তি করে পুরো মাল্টিপ্রসেসিং ইন্টারফেস সরবরাহ করে।


5
সেটা খুবই ভালো. মূল থ্রেডের বাইরে থ্রেডপুলগুলি তৈরি করতে আমার সমস্যা হয়েছিল, আপনি একবার তৈরি করা চাইল্ড থ্রেড থেকে এগুলি ব্যবহার করতে পারেন। আমি এটির জন্য একটি ইস্যু রেখেছি
ওলসন

82
এই ক্লাসটির কোনও ডকুমেন্টেশন নেই কেন আমি তা পাই না। এই ধরনের সহায়ক ক্লাসগুলি আজকাল এত গুরুত্বপূর্ণ।
ওয়ার্নাইট

18
@ ওয়ার্নাইট: এটি প্রাথমিকভাবে প্রকাশ্য নয় কারণ কেউ প্যাচ অফার করেনি যা এটি থ্রেডিং হিসাবে সরবরাহ করে (বা অনুরূপ কিছু) .ড্রেডপুল, ডকুমেন্টেশন এবং পরীক্ষাসহ। স্ট্যান্ডার্ড লাইব্রেরিতে অন্তর্ভুক্ত করা সত্যিই ভাল ব্যাটারি হবে তবে কেউ এটি না লিখলে তা ঘটবে না। মাল্টিপ্রসেসিং এই বিদ্যমান বাস্তবায়ন এক চমৎকার সুবিধা হল, এটি এমন কোন থ্রেডিং প্যাচ করা উচিত অনেক লিখন (আরো সহজ docs.python.org/devguide )
ncoghlan

3
@ ড্যানিয়েল উদিন্ডি: multiprocessing.dummy.Pool/ multiprocessing.pool.ThreadPoolএকই জিনিস এবং এটি উভয় থ্রেড পুল। তারা একটি প্রক্রিয়া পুলের ইন্টারফেসটি নকল করে তবে থ্রেডিংয়ের ক্ষেত্রে এগুলি সম্পূর্ণ প্রয়োগ করা হয়। দস্তাবেজগুলি আবার পড়ুন, আপনি এটি পিছনের দিকে পেয়েছেন।
শ্যাডোর্যাঞ্জার

9
@ daniel.gindi: আরও পড়ুন : " multiprocessing.dummyএর এপিআই-র প্রতিরূপ তৈরি করে multiprocessingতবে threadingমডিউলটির চারপাশে মোড়ক ছাড়া আর কিছু নয় " " multiprocessingসাধারণত প্রক্রিয়াগুলি সম্পর্কে, তবে প্রক্রিয়া এবং থ্রেডগুলির মধ্যে স্যুইচ করার অনুমতি দেওয়ার জন্য, তারা (বেশিরভাগ) multiprocessingএপিআই-র প্রতিরূপ তৈরি করেছিল multiprocessing.dummy, তবে থ্রেডগুলির সাহায্যে প্রক্রিয়া নয় not লক্ষ্যটি হ'ল import multiprocessing.dummy as multiprocessingপ্রক্রিয়া-ভিত্তিক কোডটি থ্রেড-ভিত্তিতে পরিবর্তন করতে আপনাকে অনুমতি দেওয়া to
শ্যাডোর্যাঞ্জার

235

পাইথন 3 এ আপনি ব্যবহার করতে পারেন concurrent.futures.ThreadPoolExecutor, যেমন:

executor = ThreadPoolExecutor(max_workers=10)
a = executor.submit(my_function)

আরও তথ্য এবং উদাহরণের জন্য দস্তাবেজগুলি দেখুন ।


6
ব্যাকপোর্টেড ফিউচার মডিউলটি ব্যবহার করতে, রান করুনsudo pip install futures
ইয়ার

এটি মাল্টি প্রসেসিংয়ের জন্য সবচেয়ে দক্ষ এবং দ্রুততম উপায়
হরিসিংহ গোহিল

2
ব্যবহার ThreadPoolExecutorএবং এর মধ্যে পার্থক্য কি multiprocessing.dummy.Pool?
জে

2
একযোগে.ফিউচার আমদানি থ্রেডপুলএক্সেক্টর
স্ট্যাক ওভারলর্ড

63

হ্যাঁ, এবং মনে হয় এটি একই (কম বেশি) একই এপিআই করেছে।

import multiprocessing

def worker(lnk):
    ....    
def start_process():
    .....
....

if(PROCESS):
    pool = multiprocessing.Pool(processes=POOL_SIZE, initializer=start_process)
else:
    pool = multiprocessing.pool.ThreadPool(processes=POOL_SIZE, 
                                           initializer=start_process)

pool.map(worker, inputs)
....

9
এর জন্য আমদানির পথটি ThreadPoolপৃথক Pool। সঠিক আমদানি হয় from multiprocessing.pool import ThreadPool
মেরিগোল্ড

2
আশ্চর্যের বিষয় হল এটি কোনও নথিভুক্ত এপিআই নয়, এবং মাল্টিপ্রসেসিং.পুলটি কেবল সংক্ষেপে অ্যাসিঙ্করসাল্ট সরবরাহ হিসাবে উল্লেখ করা হয়েছে। তবে এটি 2.x এবং 3.x এ উপলব্ধ
মার্ভিন

2
এই আমি খুঁজছিলাম ছিল। এটি কেবলমাত্র একটি একক আমদানি লাইন এবং আমার বিদ্যমান পুল লাইনে একটি ছোট পরিবর্তন এবং এটি পুরোপুরি কার্যকর হয়।
ড্যানিগ্রাফিক্স

39

খুব সাধারণ এবং হালকা ওজনের জন্য ( এখান থেকে কিছুটা সংশোধন করা ):

from Queue import Queue
from threading import Thread


class Worker(Thread):
    """Thread executing tasks from a given tasks queue"""
    def __init__(self, tasks):
        Thread.__init__(self)
        self.tasks = tasks
        self.daemon = True
        self.start()

    def run(self):
        while True:
            func, args, kargs = self.tasks.get()
            try:
                func(*args, **kargs)
            except Exception, e:
                print e
            finally:
                self.tasks.task_done()


class ThreadPool:
    """Pool of threads consuming tasks from a queue"""
    def __init__(self, num_threads):
        self.tasks = Queue(num_threads)
        for _ in range(num_threads):
            Worker(self.tasks)

    def add_task(self, func, *args, **kargs):
        """Add a task to the queue"""
        self.tasks.put((func, args, kargs))

    def wait_completion(self):
        """Wait for completion of all the tasks in the queue"""
        self.tasks.join()

if __name__ == '__main__':
    from random import randrange
    from time import sleep

    delays = [randrange(1, 10) for i in range(100)]

    def wait_delay(d):
        print 'sleeping for (%d)sec' % d
        sleep(d)

    pool = ThreadPool(20)

    for i, d in enumerate(delays):
        pool.add_task(wait_delay, d)

    pool.wait_completion()

টাস্ক সমাপ্তিতে কলব্যাকগুলি সমর্থন করার জন্য আপনি কেবল টাস্ক টিপলটিতে কলব্যাক যোগ করতে পারেন।


নিঃশর্তভাবে অসীম লুপ থাকলে থ্রেডগুলি কীভাবে যুক্ত হতে পারে?
জোসেফ গারভিন

@ জোসেফগারভিন আমি এটি পরীক্ষা করেছি এবং Queue.get()প্রোগ্রামটি শেষ হওয়া অবধি থ্রেডগুলি একটি খালি কাতারে ব্লক করে রাখে (যেহেতু কলটি ব্লক করা হচ্ছে), তারপরে সেগুলি স্বয়ংক্রিয়ভাবে বন্ধ হয়ে যায়।
ফোরামুলেটর

@ জোসেফগারভিন, ভাল প্রশ্ন। কর্মী থ্রেড নয়Queue.join() , কার্যত কাতারে যোগদান করবে । সুতরাং, যখন সারিটি খালি থাকে, রিটার্ন, প্রোগ্রাম শেষ হয় এবং থ্রেডগুলি ওএস দ্বারা কাটা হয়। wait_completion
এলোমেলির

যদি এই কোডের সমস্তটি একটি ঝরঝরে ফাংশনে আবৃত থাকে তবে সারি খালি থাকলে এবং pool.wait_completion()ফিরে আসার পরেও থ্রেডগুলি থামানো হবে বলে মনে হয় না । ফলস্বরূপ থ্রেডগুলি কেবল বিল্ডিং রাখে।
সর্বশেষে

16

পাইথনের থ্রেড পুল ব্যবহার করতে হাই আপনি এই লাইব্রেরিটি ব্যবহার করতে পারেন:

from multiprocessing.dummy import Pool as ThreadPool

এবং তারপরে ব্যবহারের জন্য, এই লাইব্রেরিটি এরকম করুন:

pool = ThreadPool(threads)
results = pool.map(service, tasks)
pool.close()
pool.join()
return results

থ্রেডগুলি আপনার পছন্দ মতো থ্রেডের সংখ্যা এবং কার্যগুলি সেই কাজের তালিকায় রয়েছে যেগুলি পরিষেবাতে সর্বাধিক মানচিত্র।


ধন্যবাদ, এটি একটি দুর্দান্ত পরামর্শ! ডক্স থেকে: মাল্টিপ্রসেসিং.ডমি মাল্টিপ্রসেসিংয়ের এপিআই প্রতিলিপি করে তবে থ্রেডিং মডিউলটির চারপাশে মোড়কের চেয়ে বেশি কিছু নয় is একটি সংশোধন - আমি মনে করি আপনি বলতে চান যে পুল এপিআই (ফাংশন, পুনরাবৃত্ত)
লেজার

2
আমরা কল .close()এবং .join()কলগুলি মিস করেছি এবং এর ফলে .map()সমস্ত থ্রেড শেষ হওয়ার আগেই শেষ হয়ে যায়। শুধু একটি সতর্কতা।
আনাতোলি শেহেরবাকভ

8

এখানে ফলাফলটি শেষ পর্যন্ত আমি ব্যবহার করে শেষ করেছি। এটি উপরের ডিজোরিসেন দ্বারা ক্লাসগুলির একটি পরিবর্তিত সংস্করণ।

ফাইল: threadpool.py

from queue import Queue, Empty
import threading
from threading import Thread


class Worker(Thread):
    _TIMEOUT = 2
    """ Thread executing tasks from a given tasks queue. Thread is signalable, 
        to exit
    """
    def __init__(self, tasks, th_num):
        Thread.__init__(self)
        self.tasks = tasks
        self.daemon, self.th_num = True, th_num
        self.done = threading.Event()
        self.start()

    def run(self):       
        while not self.done.is_set():
            try:
                func, args, kwargs = self.tasks.get(block=True,
                                                   timeout=self._TIMEOUT)
                try:
                    func(*args, **kwargs)
                except Exception as e:
                    print(e)
                finally:
                    self.tasks.task_done()
            except Empty as e:
                pass
        return

    def signal_exit(self):
        """ Signal to thread to exit """
        self.done.set()


class ThreadPool:
    """Pool of threads consuming tasks from a queue"""
    def __init__(self, num_threads, tasks=[]):
        self.tasks = Queue(num_threads)
        self.workers = []
        self.done = False
        self._init_workers(num_threads)
        for task in tasks:
            self.tasks.put(task)

    def _init_workers(self, num_threads):
        for i in range(num_threads):
            self.workers.append(Worker(self.tasks, i))

    def add_task(self, func, *args, **kwargs):
        """Add a task to the queue"""
        self.tasks.put((func, args, kwargs))

    def _close_all_threads(self):
        """ Signal all threads to exit and lose the references to them """
        for workr in self.workers:
            workr.signal_exit()
        self.workers = []

    def wait_completion(self):
        """Wait for completion of all the tasks in the queue"""
        self.tasks.join()

    def __del__(self):
        self._close_all_threads()


def create_task(func, *args, **kwargs):
    return (func, args, kwargs)

পুলটি ব্যবহার করতে

from random import randrange
from time import sleep

delays = [randrange(1, 10) for i in range(30)]

def wait_delay(d):
    print('sleeping for (%d)sec' % d)
    sleep(d)

pool = ThreadPool(20)
for i, d in enumerate(delays):
    pool.add_task(wait_delay, d)
pool.wait_completion()

অন্যান্য পাঠকদের জন্য Annotion: এই কোড পাইথন 3 (কুঁড়েঘর হয় #!/usr/bin/python3)
ড্যানিয়েল Marschall

আপনি কেন ব্যবহার করেন for i, d in enumerate(delays):এবং তার পরে iমানটিকে উপেক্ষা করবেন ?
মার্টিনো

@ মার্টিনো - সম্ভবত উন্নয়নের কেবল একটি প্রতীক যেখানে তারা সম্ভবত iকোনও রান চলাকালীন মুদ্রণ করতে চেয়েছিল ।
n1k31t4

কেন create_taskআছে? এটি কিসের জন্যে?
মিঃআর

আমি বিশ্বাস করতে পারি না এবং এসও-তে 4 টি ভোট দিয়ে উত্তর দিতে পারি পাইথনের থ্রেডপুলিং করার উপায়। সরকারী পাইথন বিতরণে থ্রেডপুলটি এখনও ভেঙে গেছে? আমি কী মিস করছি?
মিঃআর

2

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


5
যদি প্রশ্নকর্তা উইন্ডোজ অধীনে থাকেন (যা আমি বিশ্বাস করি না যে তিনি নির্দিষ্ট করেছেন), তবে আমি মনে করি যে প্রক্রিয়া স্পিনআপ একটি উল্লেখযোগ্য ব্যয় হতে পারে। অন্তত আমি যে প্রকল্পগুলি করছি তা অন্তত এটির কাজ। :-)
ব্র্যান্ডন রোডস

1

থ্রেড ভিত্তিক পুলটিতে কোনও বিল্ট নেই। তবে Queueক্লাসের সাথে প্রযোজক / গ্রাহক সারিটি প্রয়োগ করা খুব দ্রুত হতে পারে ।

থেকে: https://docs.python.org/2/library/queue.html

from threading import Thread
from Queue import Queue
def worker():
    while True:
        item = q.get()
        do_work(item)
        q.task_done()

q = Queue()
for i in range(num_worker_threads):
     t = Thread(target=worker)
     t.daemon = True
     t.start()

for item in source():
    q.put(item)

q.join()       # block until all tasks are done

3
concurrent.futuresমডিউলটির ক্ষেত্রে এটি আর হয় না ।
থানাটোস

11
আমি আর এটিকে সত্য বলে মনে করি না। from multiprocessing.pool import ThreadPool
র‌্যান্ডাল হান্ট

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.