동시.Python 3에서의 미래 vs 멀티프로세싱
Python 3.2는 Concurrent Futures를 도입했습니다.이것은 오래된 스레드화 모듈과 멀티프로세서 모듈의 고급 조합으로 보입니다.
오래된 멀티프로세서모듈에 비해 CPU바인드 태스크에 이것을 사용하는 장점과 단점은 무엇입니까?
이 기사에 따르면 이 제품들은 훨씬 사용하기 쉽다고 합니다.그런가요?
안 부르겠어요.concurrent.futures
보다 "고급" - 기본 병렬화 기법으로 여러 스레드를 사용하든 여러 프로세스를 사용하든 상관없이 거의 동일하게 동작하는 단순한 인터페이스입니다.
따라서 거의 모든 "심플한 인터페이스"와 마찬가지로 동일한 단점이 있습니다.대부분은 학습할 수 있는 것이 적기 때문에 학습 곡선이 얕아집니다.다만, 선택지가 적기 때문에, 결과적으로 풍부한 인터페이스에서는 얻을 수 없는 불편함이 생길 수 있습니다.
CPU에 바인딩된 태스크에 관한 한, 이는 너무 과소 지정되어 의미가 없습니다.CPython에서 CPU에 바인딩된 작업의 경우 속도를 높일 수 있도록 여러 스레드가 아닌 여러 프로세스가 필요합니다.그러나 속도가 얼마나 향상되는지는 하드웨어의 상세, OS, 특히 특정 태스크에 필요한 프로세스 간 통신량에 따라 달라집니다.여기에서는 모든 프로세스 간 병렬화 전략은 동일한 OS 프리미티브에 의존합니다.이러한 기능에 액세스하기 위해 사용하는 고급 API는 수익 속도의 주요 요소가 아닙니다.
편집: 예
참조하신 기사에 기재되어 있는 최종 코드는 다음과 같습니다만, 기능하기 위해서 필요한 Import 스테이트먼트를 추가합니다.
from concurrent.futures import ProcessPoolExecutor
def pool_factorizer_map(nums, nprocs):
# Let the executor divide the work among processes by using 'map'.
with ProcessPoolExecutor(max_workers=nprocs) as executor:
return {num:factors for num, factors in
zip(nums,
executor.map(factorize_naive, nums))}
multiprocessing
★★★★
import multiprocessing as mp
def mp_factorizer_map(nums, nprocs):
with mp.Pool(nprocs) as pool:
return {num:factors for num, factors in
zip(nums,
pool.map(factorize_naive, nums))}
「 」를 사용하는 .multiprocessing.Pool
오브젝트가 컨텍스트 매니저로서 Python 3.3에 추가되었습니다.
어느 쪽이 사용하기 쉬운지에 대해서는 기본적으로 동일합니다.
한 가지 다른 점이 있다면Pool
매우 다양한 방법으로 작업을 수행할 수 있기 때문에 학습 곡선을 따라 올라가기 전에는 얼마나 쉬운지 알 수 없습니다.
다시 말하지만, 이 모든 다른 방법들은 강점이자 약점이다.상황에 따라서는 유연성이 요구될 수 있기 때문에 강점이 됩니다.그들은 "가급적이면 한 가지 확실한 방법만"으로 인해 약점이 되고 있다.에 으로 달라붙는 concurrent.futures
최소 API를 사용할 수 있는 방법에 대한 불필요한 신규성이 부족하기 때문에 장기적으로 유지하기가 더 쉬울 것입니다.
한 경우 이중 하나가 됩니다.ProcessPoolExecutor
의 concurrent.futures
또는 " " " 입니다.Pool
의 multiprocessing
모듈은 동등한 설비를 제공할 것이며, 이는 결국 개인의 선호도에 관한 문제로 귀결됩니다.다만, 각 서비스에서는, 특정의 처리를 보다 편리하게 하는 몇개의 설비가 제공되고 있습니다. 넘어가야 : ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★」
작업 배치를 제출할 때 작업 결과(즉, 반환 값)를 사용할 수 있게 되는 즉시 가져오기를 원할 수 있습니다.두 기능 모두 콜백메커니즘을 통해 전송된 태스크의 결과를 이용할 수 있음을 통지합니다.
「」를 사용합니다.multiprocessing.Pool
:
import multiprocessing as mp
def worker_process(i):
return i * i # square the argument
def process_result(return_value):
print(return_value)
def main():
pool = mp.Pool()
for i in range(10):
pool.apply_async(worker_process, args=(i,), callback=process_result)
pool.close()
pool.join()
if __name__ == '__main__':
main()
을 사용하는 으로 할 수중에 콜백을 사용할 수 .concurrent.futures
:
import concurrent.futures
def worker_process(i):
return i * i # square the argument
def process_result(future):
print(future.result())
def main():
executor = concurrent.futures.ProcessPoolExecutor()
futures = [executor.submit(worker_process, i) for i in range(10)]
for future in futures:
future.add_done_callback(process_result)
executor.shutdown()
if __name__ == '__main__':
main()
서는 각 합니다.Future
인스턴스가 반환됩니다. 다음 을 '하다'에 .Future
될 때 는 ""입니다Future
및 메서드 """의 인스턴스"입니다.result
실제 반환값을 얻으려면 를 호출해야 합니다. ,를 concurrent.futures
실제로는 콜백을 사용할 필요가 없습니다..as_completed
★★★★
import concurrent.futures
def worker_process(i):
return i * i # square the argument
def main():
with concurrent.futures.ProcessPoolExecutor() as executor:
futures = [executor.submit(worker_process, i) for i in range(10)]
for future in concurrent.futures.as_completed(futures):
print(future.result())
if __name__ == '__main__':
main()
.worker_process
을 하여 을 Future
★★★★★★★★★★★★★★★★★★:
import concurrent.futures
def worker_process(i):
return i * i # square the argument
def main():
with concurrent.futures.ProcessPoolExecutor() as executor:
futures = {executor.submit(worker_process, i): i for i in range(10)}
for future in concurrent.futures.as_completed(futures):
i = futures[future] # retrieve the value that was squared
print(i, future.result())
if __name__ == '__main__':
main()
multiprocessing.Pool
가 imap
★★★★★★★★★★★★★★★★★」imap_unordered
후자는 작업 결과를 임의의 순서로 반환할 수 있지만 반드시 완료 순서는 아닙니다.이 방법들은 보다 게으른 버전의 것으로 간주됩니다.map
. . . . . 。map
passed itable 인수에 다음이 없는 경우__len__
속죄하다list
그리고 그것의 길이는 유효성을 계산하는데 사용될 것이다.chunksize
None
는 chunksize 인수로 제공되었습니다.따라서 생성기 또는 생성기 식을 반복식으로 사용하여 저장소의 최적화를 달성할 수 없습니다.하지만 방법으로는imap
★★★★★★★★★★★★★★★★★」imap_unordered
반복 가능은 생성기 또는 생성기 식일 수 있습니다. 제출을 위한 새 작업을 생성하기 위해 필요에 따라 반복됩니다.그러나 일반적으로 반복 가능한 길이를 알 수 없기 때문에 기본 청크즈 파라미터는 1이어야 합니다.하지만 그렇다고 해서 이 알고리즘과 동일한 알고리즘을 사용하여 합리적인 값을 제공하는 것을 막을 수는 없습니다.multiprocessing.Pool
반복 가능한 길이(또는 아래 예시와 같은 정확한 크기)에 대한 근사치가 적절한 경우 클래스가 사용합니다.
import multiprocessing as mp
def worker_process(i):
return i * i # square the argument
def compute_chunksize(pool_size, iterable_size):
if iterable_size == 0:
return 0
chunksize, extra = divmod(iterable_size, pool_size * 4)
if extra:
chunksize += 1
return chunksize
def main():
cpu_count = mp.cpu_count()
N = 100
chunksize = compute_chunksize(cpu_count, N)
with mp.Pool() as pool:
for result in pool.imap_unordered(worker_process, range(N), chunksize=chunksize):
print(result)
if __name__ == '__main__':
main()
,가 있으면imap_unordered
워커 프로세스가 원래의 콜 인수를 반환값과 함께 반환하지 않는 한 결과를 송신된 작업과 쉽게 관련지을 수 없습니다., 른른른, a a a a를 지정할 수 .chunksize
imap_unordered
★★★★★★★★★★★★★★★★★」imap
예측 가능한 순서로 결과를 얻을 수 있는 경우, 에서는, 이러한 메서드를 보다 효율적으로 실행할 필요가 있습니다.apply_async
기본적으로 1의 청크기를 사용하는 것과 동등합니다. 반드시 이 방법을 .apply_async
콜백 기능이 있습니다.단, 실험 결과에 따르면 청크사이즈 값 1을 사용하여imap_unordered
결과는 완료 순서로 반환됩니다.
map
의 of의 ProcessPoolExecutor
의 concurrent.futures
합니다.Pool.imap
의 multiprocessing
패키지.이 메서드에서는 유효한 청크사이즈 값을 계산하기 위해 generator 식인 반복 가능한 인수를 목록으로 변환하지 않습니다.따라서 chunksize 인수는 기본값이 1로 설정되어 있습니다.또한 큰 반복가능성을 전달할 경우에는 적절한 청크사이즈 값을 지정하는 것을 검토해야 합니다.단, 와는 달리Pool.imap
에 의해 반환된 반복 가능을 반복함으로써 첫 번째 결과를 얻을 수 없다는 것이 제 경험입니다.map
모든 반복 가능한 것이 전달될 때까지map
완전히 반복되었습니다.
multiprocessing.Pool
에는 메서드가 .apply
작업을 풀에 제출하고 결과가 준비될 때까지 차단합니다.은 return worker에 입니다.apply
를 들어 다음과 같습니다예를 들어 다음과 같습니다.
import multiprocessing as mp
def worker_process(i):
return i * i # square the argument
def main():
with mp.Pool() as pool:
print(pool.apply(worker_process, args=(6,)))
print(pool.apply(worker_process, args=(4,)))
if __name__ == '__main__':
main()
concurrent.futures.ProcessPoolExecutor
클래스에는 해당 항목이 없습니다..submit
에, 「 」, 「 」에의 콜을 실시합니다.result
된 (Future
례 it 해야 게 게 아니라 렇렇해 but but but but but but but but but but but but but but but but but but but but but but but but but but.Pool.apply
방법은 차단 작업 제출이 적절한 활용 사례에 더 편리합니다.이러한 경우 스레드에서 실행되는 작업의 대부분은 I/O가 많기 때문에 스레드를 필요로 하는 처리가 있는 경우가 해당됩니다.단, CPU가 매우 바인드 되어 있는 기능을 1개 제외합니다.를 처음 됩니다.multiprocessing.Pool
모든 스레드에 인수로 전달합니다.할 경우 바운드를 하여 기능을 합니다.Pool.apply
다른 프로세스에서 코드를 실행하고 현재 프로세스를 해방하여 다른 스레드를 실행할 수 있도록 합니다.
큰일이 일어났다.concurrent.futures
2개의 클래스, 2개의 클래스, 2개의 모듈,ProcessPoolExecutor
★★★★★★★★★★★★★★★★★」ThreadPoolExecutor
같은 인터페이스를 사용합니다.그것은 좋은 특징입니다.그...multiprocessing
되어 있지 않은 「문서화되어 있지 않다」가 .ThreadPool
「」와 같은 를 가지는 .Pool
:
>>> from multiprocessing.pool import Pool
>>> from multiprocessing.pool import ThreadPool
>>> dir(Pool)
['Process', '__class__', '__del__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_check_running', '_get_sentinels', '_get_tasks', '_get_worker_sentinels', '_guarded_task_generation', '_handle_results', '_handle_tasks', '_handle_workers', '_help_stuff_finish', '_join_exited_workers', '_maintain_pool', '_map_async', '_repopulate_pool', '_repopulate_pool_static', '_setup_queues', '_terminate_pool', '_wait_for_updates', '_wrap_exception', 'apply', 'apply_async', 'close', 'imap', 'imap_unordered', 'join', 'map', 'map_async', 'starmap', 'starmap_async', 'terminate']
>>> dir(ThreadPool)
['Process', '__class__', '__del__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_check_running', '_get_sentinels', '_get_tasks', '_get_worker_sentinels', '_guarded_task_generation', '_handle_results', '_handle_tasks', '_handle_workers', '_help_stuff_finish', '_join_exited_workers', '_maintain_pool', '_map_async', '_repopulate_pool', '_repopulate_pool_static', '_setup_queues', '_terminate_pool', '_wait_for_updates', '_wrap_exception', 'apply', 'apply_async', 'close', 'imap', 'imap_unordered', 'join', 'map', 'map_async', 'starmap', 'starmap_async', 'terminate']
>>>
하다, 하다, 하다, 하다, 하다, 하다를 사용해서 할 수 .ProcessPoolExecutor.submit
되는 값입니다.Future
"instance" 입니다.Pool.apply_async
반환됩니다.AsyncResult
및 합니다.
from concurrent.futures import ProcessPoolExecutor, TimeoutError
from time import sleep
def worker_1():
while True:
print('hanging')
sleep(1)
def main():
with ProcessPoolExecutor(1) as pool:
future = pool.submit(worker_1)
try:
future.result(3) # kill task after 3 seconds?
except TimeoutError:
print('timeout')
if __name__ == '__main__':
main()
print("return from main()")
인쇄:
hanging
hanging
hanging
timeout
hanging
hanging
hanging
hanging
hanging
hanging
hanging
etc.
를 호출할 future.result(3)
TimeoutError
제출된 작업이 해당 기간 내에 완료되지 않았기 때문에 3초 후에 예외가 발생합니다. up up up but but 、 up up 、 up but 、 but but 、 but but but 。with ProcessPoolExecutor(1) as pool:
블록이 종료되지 않기 때문에 프로그램이 종료되지 않습니다.
from multiprocessing import Pool, TimeoutError
from time import sleep
def worker_1():
while True:
print('hanging')
sleep(1)
def main():
with Pool(1) as pool:
result = pool.apply_async(worker_1, args=())
try:
result.get(3) # kill task after 3 seconds?
except TimeoutError:
print('timeout')
if __name__ == '__main__':
main()
print("return from main()")
인쇄:
hanging
hanging
hanging
timeout
return from main()
도, 이 은 계속 진행됩니다.with
블록이 종료되는 것을 막지 않기 때문에 프로그램은 정상적으로 종료됩니다.는 의 Pool
는 instance에 합니다.terminate
블록이 종료되고 이로 인해 풀 내의 모든 프로세스가 즉시 종료됩니다.은, 의 됩니다.ProcessPoolExecutor
인스턴스, 인스턴스에 전화:콜을 실행합니다를 시행하고 있습니다.shutdown(wait=True)
수영장에서 그것을 지배하는 블록 출구까지의 과정의 종료를 기다립니다.풀에서 제어하는 블록이 종료될 때 풀 내의 모든 프로세스가 종료될 때까지 기다립니다.이점은 장점이있는 것 같습니다에 갈 것 같다.multiprocessing.Pool
컨텍스트 핸들러를 사용하여 풀 종료를 처리하고 있으며 타임아웃 가능성이 있는 경우.업데이트:Python 3.9에서는 cancel_futures라는 새로운 인수가 추가되어 있습니다.shutdown
방법.만약 당신이 명시적으로 실행 중인 모든 따라서 부르방법. 결과적으로, 여러분은 태스크 및 실행 대기 중인 모든 태스크를 명시적으로 호출하면 종료할 수 있습니다 실행 중인 모든 작업과 어떤 작업이 실행되기를 기다리고 해지할 수 있다.shutdown(wait=False, cancel_futures=True)
대신 기본 동작이 내재된 전화 에의 암묵적인 콜에 의한 디폴트 동작에 의존하지 않고에 이르에 의존하는 것이.shutdown
언제는 컨텍스트 처리기를 사용하여.콘텍스트핸들러를 사용하는 경우.
하지만하지만 콘텍스트핸들러가에 대한 컨텍스트를 처리기 이후.multiprocessing.Pool
때만 전화를 하콜만terminate
아니라 가 아니라close
이어서가 그 뒤를 이었다join
그때 당신이 제출한 바 있는 모든 업무 전송한 모든 작업이 완료되었는지 확인한 후 를 종료해야 합니다는 그런 다음 끝내기 전에 마치도록 해야 한다.with
블록 예에 예를 들어 블로킹과 동기 콜을 포함한 작업을 송신하는 등의 방법으로 블록합니다와 같은 차단, 동기 호출이 일자리를 제출하여.map
또는 또는 호출 전화get
AsyncResult
에 .apply_async
콜 한다.imap
""를 close
에 어 followed가 붙는다.join
츠요시
가 완료될 까지 종료할 단, 타임아웃된 할 수 없습니다.ProcessPoolExecutor
아직 실행 중이 아닌 제출된 작업의 시작을 취소할 수 있습니다.다음 데모에서는 작업을 연속적으로만 실행할 수 있도록 크기 1의 풀이 있습니다.3개의 작업을 연속해서 송신하고 있습니다.처음 2개의 작업을 실행하는 데 3초가 걸립니다.이 작업에서는, 다음의 콜이 있기 때문입니다.time.sleep(3)
처음 두 작업을 즉시 취소하려고 합니다.첫 번째 작업이 이미 실행 중이기 때문에 첫 번째 취소 시도가 실패합니다.그러나 풀에는 프로세스가 1개뿐이므로 두 번째 작업은 첫 번째 작업이 완료될 때까지 3초 동안 기다려야 실행이 시작되므로 취소가 성공합니다.마지막으로, 작업 3은 작업1이 완료된 직후에 시작되어 종료됩니다.이는 작업 제출을 시작한 지 약 3초 후입니다.
from concurrent.futures import ProcessPoolExecutor
import time
def worker1(i):
time.sleep(3)
print('Done', i)
def worker2():
print('Hello')
def main():
with ProcessPoolExecutor(max_workers=1) as executor:
t = time.time()
future1 = executor.submit(worker1, 1)
future2 = executor.submit(worker1, 2)
future3 = executor.submit(worker2)
# this will fail since this task is already running:
print(future1.cancel())
# this will succeed since this task hasn't started (it's waiting for future1 to complete):
print(future2.cancel())
future3.result() # wait for completion
print(time.time() - t)
if __name__ == '__main__':
main()
인쇄:
False
True
Done 1
Hello
3.1249606609344482
다른 답변의 자세한 차이점 목록과 더불어, 저는 개인적으로 멀티프로세스로 발생할 수 있는 고정되지 않은(2020-10-27년 기준)의 무기한 행에 부딪혔습니다.작업자 중 한 명이 특정 방식으로 충돌했을 때 풀링합니다.(내 경우, sython 내선번호의 예외입니다만, 작업자가 SIGTERM 등을 취득했을 경우에 발생할 가능성이 있다고 하는 사람도 있습니다).ProcessPoolExecutor의 설명서에 따르면 python 3.3부터 이 기능에 대해 견고합니다.
제 경험상 멀티프로세싱 모듈은 동시 처리와 비교하여 많은 문제에 직면했습니다.선물용입니다.(단, Windows OS에서 사용)
내가 알 수 있는 두 가지 주요 차이점은 다음과 같다.
- 멀티프로세서 모듈에서의 빈번한 행업
- 동시.선물은 비교적 간단한 실행 방법을 가지고 있다.즉, 결과를 가져오고 하위 프로세스를 추적하는 것은 매우 간단합니다. etc.is
예: (결과 가져오기)
with concurrent.futures.ProcessPoolExecutor() as executor:
f1 = executor.submit(some_function, parameter_to_be_passed)
print(f1.result())
그래서 만약 당신이 어떤 값이라도 돌려준다면some_function()
잡아서 할 수 .f1.result()
이와 마찬가지로 "멀티프로세싱" 모듈의 추가 단계가 필요합니다.
Linux 시스템에서 실행 중인 경우 행이 발생하지 않을 수 있지만 "멀티프로세싱" 모듈에서는 여전히 실행이 복잡합니다.
또, 이 점에 주의해 주세요.이 작업은 CPU 부하가 높은 작업입니다.
개인적으로는 동시 사용을 추천합니다.선물용입니다.
는 ★★★★★★★★★★★★★★★★★★★★.concurrent.futures
는 주로 (복수에 의한 것입니다.multiprocessing
함수에 여러 할 때 합니다(이러한 인수는 ).istarmap()
- - - - - - - - - 。starmap()
import multiprocessing as mp
def power_plus_one(x, y):
return (x**y) + 1
def wrapper(t):
return power_plus_one(*t)
with mp.Pool() as pool:
r = list(pool.imap(wrapper, [(0, 1), (2, 2)]))
print(r)
imap()
/imap_unordered()
또는 더 큰 계산을 위한 시간 추정과 같은 진행 표시줄에 매우 유용합니다.인concurrents.futures
def power_plus_one(x, y):
return (x**y) + 1
o = dict() # dict for output
with concurrent.futures.ProcessPoolExecutor() as executor:
futures = {executor.submit(power_plus_one, x, y): (x, y) for x, y in [(0, 1), (2, 2)]}
for future in concurrent.futures.as_completed(futures):
i = futures[future]
o[i] = future.result()
print(o)
또한 편리한 결과 매핑을 명령어로 사용하는 것도 좋아합니다.:)
tqdm을 사용하면 다음 작업을 쉽게 수행할 수 있습니다.
for future in tqdm(concurrent.futures.as_completed(futures), total=len(futures)):
...
concurrent.futures
예를 들어 다음과 같은 제어 기능을 제공합니다.
# Created by BaiJiFeiLong@gmail.com at 2021/10/19 10:37
import concurrent.futures
import multiprocessing.pool
import random
import threading
import time
def hello(name):
time.sleep(random.random())
return f"Hello {name} {threading.current_thread()} "
print("ThreadPool:")
pool = multiprocessing.pool.ThreadPool(4)
for args, result in pool.imap_unordered(lambda x: (x, hello(x)), range(10)):
print(args, "=>", result)
print("\nThreadPoolExecutor:")
executor = concurrent.futures.ThreadPoolExecutor(max_workers=4)
futures = {executor.submit(hello, x): x for x in range(10)}
for future in concurrent.futures.as_completed(futures):
print(futures[future], "=>", future.result()
출력 예:
ThreadPool:
1 => Hello 1 <DummyProcess(Thread-2, started daemon 29700)>
0 => Hello 0 <DummyProcess(Thread-1, started daemon 29688)>
2 => Hello 2 <DummyProcess(Thread-3, started daemon 19680)>
6 => Hello 6 <DummyProcess(Thread-3, started daemon 19680)>
3 => Hello 3 <DummyProcess(Thread-4, started daemon 33028)>
4 => Hello 4 <DummyProcess(Thread-2, started daemon 29700)>
5 => Hello 5 <DummyProcess(Thread-1, started daemon 29688)>
9 => Hello 9 <DummyProcess(Thread-2, started daemon 29700)>
8 => Hello 8 <DummyProcess(Thread-4, started daemon 33028)>
7 => Hello 7 <DummyProcess(Thread-3, started daemon 19680)>
ThreadPoolExecutor:
0 => Hello 0 <Thread(ThreadPoolExecutor-0_0, started daemon 30764)>
1 => Hello 1 <Thread(ThreadPoolExecutor-0_1, started daemon 36220)>
2 => Hello 2 <Thread(ThreadPoolExecutor-0_2, started daemon 13120)>
4 => Hello 4 <Thread(ThreadPoolExecutor-0_0, started daemon 30764)>
3 => Hello 3 <Thread(ThreadPoolExecutor-0_3, started daemon 30260)>
8 => Hello 8 <Thread(ThreadPoolExecutor-0_3, started daemon 30260)>
5 => Hello 5 <Thread(ThreadPoolExecutor-0_1, started daemon 36220)>
6 => Hello 6 <Thread(ThreadPoolExecutor-0_2, started daemon 13120)>
7 => Hello 7 <Thread(ThreadPoolExecutor-0_0, started daemon 30764)>
9 => Hello 9 <Thread(ThreadPoolExecutor-0_3, started daemon 30260)>
언급URL : https://stackoverflow.com/questions/20776189/concurrent-futures-vs-multiprocessing-in-python-3
'source' 카테고리의 다른 글
root@localhost를 가진 관리자로부터 "권한 거부" 오류가 발생했습니다. (0) | 2022.09.20 |
---|---|
쿼리에 오류가 없고 행도 0개 반환되는 이유는 무엇입니까? (0) | 2022.09.20 |
JavaScript에서 숫자가 홀수인지 확인하는 방법 (0) | 2022.09.20 |
특정 속성을 가진 요소를 포함한다고 단언하려면 어떻게 해야 합니다. (0) | 2022.09.20 |
xampp MySQL이 시작되지 않음 (0) | 2022.09.20 |