Python有了concurrent的话mutiprocessing和threading还有存在的意义吗?
高洛峰
高洛峰 2017-04-18 10:11:53
0
3
930

Python3.2中引入的concurrent非常的好用,只用几行代码就可以编写出线程池/进程池,并且计算型任务效率和mutiprocessing.pool提供的poll和ThreadPoll相比不分伯仲,而且在IO型任务由于引入了Future的概念效率要高数倍。

而threading的话还要自己维护相关的队列防止死锁,代码的可读性也会下降,相反concurrent提供的线程池却非常的便捷,不用自己操心死锁以及编写线程池代码,由于异步的概念IO型任务也更有优势。

既然如此,如果不是为了向下兼容2.x,是不是可以完全没有必要继续使用mutiprocessing和threading了?concurrent如此的优秀。

高洛峰
高洛峰

拥有18年软件开发和IT教学经验。曾任多家上市公司技术总监、架构师、项目经理、高级软件工程师等职务。 网络人气名人讲师,...

membalas semua(3)
阿神

Concurrent sememangnya sangat berguna, terutamanya menyediakan ThreadPoolExecutor dan ProcessPoolExecutor. Berbilang benang, berbilang proses. Tetapi serentak pada asasnya adalah enkapsulasi threading dan mutiprocessing. Anda boleh mengetahui dengan melihat kod sumbernya.
ThreadPoolExecutor menyediakan baris gilir tugasnya sendiri, jadi tidak perlu menulisnya sendiri. Kumpulan utas yang dipanggil hanya membandingkan bilangan utas semasa dengan saiz max_workers yang ditentukan Jika saiznya lebih kecil daripada max_workers, tugasan dibenarkan untuk mencipta utas untuk melaksanakan tugas. Anda boleh melihat kod sumber

def _adjust_thread_count(self):

# When the executor gets lost, the weakref callback will wake up
# the worker threads.
def weakref_cb(_, q=self._work_queue):
    q.put(None)
# TODO(bquinlan): Should avoid creating new threads if there are more
# idle threads than items in the work queue.
if len(self._threads) < self._max_workers:
    t = threading.Thread(target=_worker,
                         args=(weakref.ref(self, weakref_cb),
                               self._work_queue))
    t.daemon = True
    t.start()
    self._threads.add(t)
    _threads_queues[t] = self._work_queue
  

Jadi, jika anda mengekalkan baris gilir sendiri, ia tidak menjadi masalah Cocurrent juga mengekalkan baris gilir secara dalaman, dan ia hanya ditulis untuk anda.
Bagi masalah kebuntuan, serentak juga boleh menyebabkan masalah kebuntuan. Biar saya berikan anda satu contoh, jalankan dan lihat

import time
from concurrent.futures import ThreadPoolExecutor

def wait_on_b():
    time.sleep(5)
    print(b.result()) # b will never complete because it is waiting on a.
    return 5

def wait_on_a():
    time.sleep(5)
    print(a.result()) # a will never complete because it is waiting on b.
    return 6


executor = ThreadPoolExecutor(max_workers=2)
a = executor.submit(wait_on_b)
b = executor.submit(wait_on_a)

ProcessPoolExecutor juga menggunakan mutiprocessing secara dalaman. Ia boleh menggunakan sepenuhnya ciri-ciri berbilang teras dan menyingkirkan sekatan GIL. Ambil perhatian bahawa apabila mentakrifkan ProcessPoolExecutor(max_workers=2), max_workers adalah lebih besar sedikit daripada bilangan teras CPU dan tidak boleh terlalu besar. ProcessPoolExecutor secara dalaman mengekalkan call_queue untuk mengekalkan baris gilir tugas, jenisnya ialah multiprocessing.Queue. Terdapat juga benang yang menguruskan baris gilir. Ini boleh dikatakan sebagai pengoptimuman cocurrent.
Anda boleh melihat kod sumber untuk butiran diri._adjust_process_count() sebenarnya memulakan proses untuk melaksanakan tugasan anda boleh mengetahuinya dengan mengklik pada _adjust_process_count. self._queue_management_thread ialah utas yang menguruskan baris gilir

if self._queue_management_thread is None:
            # Start the processes so that their sentinels are known.
            self._adjust_process_count()
            self._queue_management_thread = threading.Thread(
                    target=_queue_management_worker,
                    args=(weakref.ref(self, weakref_cb),
                          self._processes,
                          self._pending_work_items,
                          self._work_ids,
                          self._call_queue,
                          self._result_queue))
            self._queue_management_thread.daemon = True
            self._queue_management_thread.start()
            _threads_queues[self._queue_management_thread] = self._result_queue

Jadi cocurrent mudah digunakan, iaitu, ia melakukan pemprosesan yang lebih baik dengan sendirinya, seperti mengekalkan baris gilir dan mengurus baris gilir, jadi anda tidak perlu risau tentangnya. Sudah tentu anda juga boleh melaksanakannya sendiri. Anda boleh mencapai ini menggunakan cocurrent. Ia boleh dicapai dengan pemprosesan benang dan mutiproses Paling teruk, anda perlu melakukan kerja tambahan sendiri. Kerana cocurrent pada asasnya menggunakan dua teras ini. Sudah tentu, adalah lebih baik jika anda mempunyai cocurrent yang lebih baik yang sudah tersedia Anda boleh menggunakannya secara langsung dan bukannya mencipta semula roda itu sendiri. Jadi yang mana satu untuk digunakan bergantung pada kebiasaan peribadi anda Sebagai contoh, saya menggunakan python2, tetapi saya tidak boleh menggunakan cocurrent. Terpaksa guna threading.

阿神

Orang di atas telah mengatakannya dengan sangat jelas, saya cuma ingin menambah sedikit.
Concurrent.future menggunakan konsep tak segerak untuk menguruskan benang/proses, tetapi ia sebenarnya tidak merangkum IO tak segerak, jadi penanya berkata Peningkatan kecekapan IO sebenarnya salah.

伊谢尔伦

Serentak ialah coroutine, bukan utas, dua konsep.

Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan