Python์ concurrent.futures ๋ชจ๋ ์ข ํฉ ๊ฐ์ด๋: ์ค๋ ๋ ๋ฐ ํ๋ก์ธ์ค ํ ๋น๊ต, ์ค์ฉ์ ์ธ ์์ ํฌํจ.
Python์์ ๋์์ฑ ์ ๊ธ ํด์ : ThreadPoolExecutor vs ProcessPoolExecutor
Python์ ๋ค์ฌ๋ค๋ฅํ๊ณ ๋๋ฆฌ ์ฌ์ฉ๋๋ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด์ด์ง๋ง, ์ ์ญ ์ธํฐํ๋ฆฌํฐ ์ ๊ธ(GIL)์ผ๋ก ์ธํด ์ง์ ํ ๋ณ๋ ฌ์ฑ ์ธก๋ฉด์์ ํน์ ํ๊ณ๊ฐ ์์ต๋๋ค. concurrent.futures
๋ชจ๋์ ํธ์ถ ๊ฐ๋ฅํ ๊ฐ์ฒด๋ฅผ ๋น๋๊ธฐ์ ์ผ๋ก ์คํํ๊ธฐ ์ํ ๊ณ ์์ค ์ธํฐํ์ด์ค๋ฅผ ์ ๊ณตํ๋ฉฐ, ์ด๋ฌํ ํ๊ณ๋ฅผ ์ผ๋ถ ๊ทน๋ณตํ๊ณ ํน์ ์ ํ์ ์์
์ฑ๋ฅ์ ํฅ์์ํค๋ ๋ฐฉ๋ฒ์ ์ ๊ณตํฉ๋๋ค. ์ด ๋ชจ๋์ ๋ ๊ฐ์ง ์ฃผ์ ํด๋์ค์ธ ThreadPoolExecutor
์ ProcessPoolExecutor
๋ฅผ ์ ๊ณตํฉ๋๋ค. ์ด ์ข
ํฉ ๊ฐ์ด๋์์๋ ๋ ๊ฐ์ง๋ฅผ ๋ชจ๋ ํ์ํ๊ณ , ์ฐจ์ด์ , ๊ฐ์ ๋ฐ ์ฝ์ ์ ๊ฐ์กฐํ๋ฉฐ, ํ์์ ๋ง๋ ์ ์ ํ ์คํ๊ธฐ๋ฅผ ์ ํํ๋ ๋ฐ ๋์์ด ๋๋ ์ค์ฉ์ ์ธ ์์ ๋ฅผ ์ ๊ณตํฉ๋๋ค.
๋์์ฑ๊ณผ ๋ณ๋ ฌ์ฑ ์ดํดํ๊ธฐ
๊ฐ ์คํ๊ธฐ์ ํน์ ๋ด์ฉ์ ์ดํด๋ณด๊ธฐ ์ ์ ๋์์ฑ๊ณผ ๋ณ๋ ฌ์ฑ์ ๊ฐ๋ ์ ์ดํดํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค. ์ด ์ฉ์ด๋ค์ ์ข ์ข ํผ์ฉ๋์ง๋ง ๋๋ ทํ ์๋ฏธ๋ฅผ ๊ฐ์ง๋๋ค.
- ๋์์ฑ: ์ฌ๋ฌ ์์ ์ ๋์์ ๊ด๋ฆฌํ๋ ๊ฒ๊ณผ ๊ด๋ จ์ด ์์ต๋๋ค. ์ค์ ๋ก ๋จ์ผ ํ๋ก์ธ์ ์ฝ์ด์์ ์ธํฐ๋ฆฌ๋น๋๋๋ผ๋ ์ฌ๋ฌ ์์ ์ ๋์์ ์ฒ๋ฆฌํ๋๋ก ์ฝ๋๋ฅผ ๊ตฌ์ฑํ๋ ๊ฒ์ ๋๋ค. ๋จ์ผ ์คํ ๋ธ์์ ์ฌ๋ฌ ๋๋น๋ฅผ ๊ด๋ฆฌํ๋ ์๋ฆฌ์ฌ๋ฅผ ์๊ฐํด๋ณด์ธ์. ๋ชจ๋ ๋๋น๊ฐ *์ ํํ* ๋์์ ๋๊ณ ์๋ ๊ฒ์ ์๋์ง๋ง, ์๋ฆฌ์ฌ๋ ๋ชจ๋ ๋๋น๋ฅผ ๊ด๋ฆฌํ๊ณ ์์ต๋๋ค.
- ๋ณ๋ ฌ์ฑ: ์ผ๋ฐ์ ์ผ๋ก ์ฌ๋ฌ ํ๋ก์ธ์ ์ฝ์ด๋ฅผ ํ์ฉํ์ฌ ์ค์ ๋ก ์ฌ๋ฌ ์์ ์ *๋์์* ์คํํ๋ ๊ฒ์ ํฌํจํฉ๋๋ค. ์ด๋ ์ฌ๋ฌ ๋ช ์ ์๋ฆฌ์ฌ๊ฐ ๋์์ ์์ฌ์ ๋ค๋ฅธ ๋ถ๋ถ์์ ์์ ํ๋ ๊ฒ๊ณผ ๊ฐ์ต๋๋ค.
Python์ GIL์ ์ค๋ ๋๋ฅผ ์ฌ์ฉํ ๋ CPU ๋ฐ์ด๋ ์์ ์ ๋ํ ์ง์ ํ ๋ณ๋ ฌ์ฑ์ ๊ฑฐ์ ๋ฐฉ์งํฉ๋๋ค. ์ด๋ GIL์ด ์ฃผ์ด์ง ์๊ฐ์ ๋จ ํ๋์ ์ค๋ ๋๋ง Python ์ธํฐํ๋ฆฌํฐ ์ ์ด๊ถ์ ๊ฐ์ง ์ ์๋๋ก ํ์ฉํ๊ธฐ ๋๋ฌธ์ ๋๋ค. ๊ทธ๋ฌ๋ ํ๋ก๊ทธ๋จ์ด ๋คํธ์ํฌ ์์ฒญ์ด๋ ๋์คํฌ ์ฝ๊ธฐ์ ๊ฐ์ ์ธ๋ถ ์์ ์ ๊ธฐ๋ค๋ฆฌ๋ ๋ฐ ๋๋ถ๋ถ์ ์๊ฐ์ ์๋นํ๋ I/O ๋ฐ์ด๋ ์์ ์ ๊ฒฝ์ฐ, ํ ์ค๋ ๋๊ฐ ๊ธฐ๋ค๋ฆฌ๋ ๋์ ๋ค๋ฅธ ์ค๋ ๋๊ฐ ์คํ๋ ์ ์๋๋ก ํ์ฌ ์ค๋ ๋๊ฐ ์ฌ์ ํ ์๋นํ ์ฑ๋ฅ ํฅ์์ ์ ๊ณตํ ์ ์์ต๋๋ค.
`concurrent.futures` ๋ชจ๋ ์๊ฐ
concurrent.futures
๋ชจ๋์ ๋น๋๊ธฐ์ ์ผ๋ก ์์
์ ์คํํ๋ ํ๋ก์ธ์ค๋ฅผ ๋จ์ํํฉ๋๋ค. ์ค๋ ๋ ๋ฐ ํ๋ก์ธ์ค์ ํจ๊ป ์์
ํ๊ธฐ ์ํ ๊ณ ์์ค ์ธํฐํ์ด์ค๋ฅผ ์ ๊ณตํ๋ฉฐ, ์ง์ ๊ด๋ฆฌํ๋ ๋ฐ ํ์ํ ๋ณต์ก์ฑ์ ๋๋ถ๋ถ์ ์ถ์ํํฉ๋๋ค. ํต์ฌ ๊ฐ๋
์ ์ ์ถ๋ ์์
์ ์คํ์ ๊ด๋ฆฌํ๋ "์คํ๊ธฐ"์
๋๋ค. ๋ ๊ฐ์ง ์ฃผ์ ์คํ๊ธฐ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
ThreadPoolExecutor
: ์์ ์ ์คํํ๊ธฐ ์ํด ์ค๋ ๋ ํ์ ํ์ฉํฉ๋๋ค. I/O ๋ฐ์ด๋ ์์ ์ ์ ํฉํฉ๋๋ค.ProcessPoolExecutor
: ์์ ์ ์คํํ๊ธฐ ์ํด ํ๋ก์ธ์ค ํ์ ํ์ฉํฉ๋๋ค. CPU ๋ฐ์ด๋ ์์ ์ ์ ํฉํฉ๋๋ค.
ThreadPoolExecutor: I/O ๋ฐ์ด๋ ์์ ์ ์ํ ์ค๋ ๋ ํ์ฉ
ThreadPoolExecutor
๋ ์์
์คํ์ ์ํด ์์ปค ์ค๋ ๋ ํ์ ์์ฑํฉ๋๋ค. GIL ๋๋ฌธ์ ์ค๋ ๋๋ ์ง์ ํ ๋ณ๋ ฌ์ฑ์ ์ด์ ์ ์ป๋ ๊ณ์ฐ ์ง์ฝ์ ์ธ ์์
์ ์ด์์ ์ด์ง ์์ต๋๋ค. ๊ทธ๋ฌ๋ I/O ๋ฐ์ด๋ ์๋๋ฆฌ์ค์์๋ ๋ฐ์ด๋ฉ๋๋ค. ์ฌ์ฉ ๋ฐฉ๋ฒ์ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ
๋ค์์ ์ฌ๋ฌ ์น ํ์ด์ง๋ฅผ ๋์์ ์ผ๋ก ๋ค์ด๋ก๋ํ๊ธฐ ์ํด ThreadPoolExecutor
๋ฅผ ์ฌ์ฉํ๋ ๊ฐ๋จํ ์์ ์
๋๋ค.
import concurrent.futures
import requests
import time
urls = [
"https://www.example.com",
"https://www.google.com",
"https://www.wikipedia.org",
"https://www.python.org"
]
def download_page(url):
try:
response = requests.get(url, timeout=5)
response.raise_for_status() # ์๋ชป๋ ์๋ต (4xx ๋๋ 5xx)์ ๋ํด HTTPError ๋ฐ์
print(f"Downloaded {url}: {len(response.content)} bytes")
return len(response.content)
except requests.exceptions.RequestException as e:
print(f"Error downloading {url}: {e}")
return 0
start_time = time.time()
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
# ๊ฐ URL์ ์คํ๊ธฐ๋ก ์ ์ถ
futures = [executor.submit(download_page, url) for url in urls]
# ๋ชจ๋ ์์
์ด ์๋ฃ๋ ๋๊น์ง ๋๊ธฐ
total_bytes = sum(future.result() for future in concurrent.futures.as_completed(futures))
print(f"Total bytes downloaded: {total_bytes}")
print(f"Time taken: {time.time() - start_time:.2f} seconds")
์ค๋ช :
- ํ์ํ ๋ชจ๋(
concurrent.futures
,requests
,time
)์ ๊ฐ์ ธ์ต๋๋ค. - ๋ค์ด๋ก๋ํ URL ๋ชฉ๋ก์ ์ ์ํฉ๋๋ค.
download_page
ํจ์๋ ์ฃผ์ด์ง URL์ ๋ด์ฉ์ ๊ฒ์ํฉ๋๋ค. `try...except`์ `response.raise_for_status()`๋ฅผ ์ฌ์ฉํ์ฌ ์ค๋ฅ ์ฒ๋ฆฌ๋ฅผ ํฌํจํ์ฌ ์ ์ฌ์ ์ธ ๋คํธ์ํฌ ๋ฌธ์ ๋ฅผ ํฌ์ฐฉํฉ๋๋ค.- ์ต๋ 4๊ฐ์ ์์ปค ์ค๋ ๋๋ฅผ ์ฌ์ฉํ์ฌ
ThreadPoolExecutor
๋ฅผ ์์ฑํฉ๋๋ค.max_workers
์ธ์๋ ๋์์ ์ฌ์ฉํ ์ ์๋ ์ต๋ ์ค๋ ๋ ์๋ฅผ ์ ์ดํฉ๋๋ค. ๋๋ฌด ๋๊ฒ ์ค์ ํ๋ฉด ํนํ ๋คํธ์ํฌ ๋์ญํญ์ด ๋ณ๋ชฉ ํ์์ธ I/O ๋ฐ์ด๋ ์์ ์์ ํญ์ ์ฑ๋ฅ์ด ํฅ์๋์ง ์์ ์ ์์ต๋๋ค. - ๋ฆฌ์คํธ ์ปดํ๋ฆฌํจ์
์ ์ฌ์ฉํ์ฌ
executor.submit(download_page, url)
์ ํตํด ๊ฐ URL์ ์คํ๊ธฐ๋ก ์ ์ถํฉ๋๋ค. ์ด๋ ๊ฐ ์์ ์ ๋ํดFuture
๊ฐ์ฒด๋ฅผ ๋ฐํํฉ๋๋ค. concurrent.futures.as_completed(futures)
ํจ์๋ ์๋ฃ๋๋ ๋๋ก ๋ฏธ๋๋ฅผ ์์ฑํ๋ ๋ฐ๋ณต์๋ฅผ ๋ฐํํฉ๋๋ค. ์ด๋ฅผ ํตํด ๊ฒฐ๊ณผ๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ ์ ๋ชจ๋ ์์ ์ด ์๋ฃ๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฌ๋ ๊ฒ์ ๋ฐฉ์งํ ์ ์์ต๋๋ค.- ์๋ฃ๋ ๋ฏธ๋๋ฅผ ๋ฐ๋ณตํ๊ณ
future.result()
๋ฅผ ์ฌ์ฉํ์ฌ ๊ฐ ์์ ์ ๊ฒฐ๊ณผ๋ฅผ ๊ฒ์ํ๊ณ ๋ค์ด๋ก๋๋ ์ด ๋ฐ์ดํธ๋ฅผ ํฉ์ฐํฉ๋๋ค. `download_page` ๋ด์ ์ค๋ฅ ์ฒ๋ฆฌ๋ ๊ฐ๋ณ ์คํจ๊ฐ ์ ์ฒด ํ๋ก์ธ์ค๋ฅผ ์ค๋จ์ํค์ง ์๋๋ก ํฉ๋๋ค. - ๋ง์ง๋ง์ผ๋ก ๋ค์ด๋ก๋๋ ์ด ๋ฐ์ดํธ์ ์์๋ ์๊ฐ์ ์ถ๋ ฅํฉ๋๋ค.
ThreadPoolExecutor์ ์ด์
- ๊ฐ์ํ๋ ๋์์ฑ: ์ค๋ ๋ ๊ด๋ฆฌ๋ฅผ ์ํ ๊น๋ํ๊ณ ์ฌ์ฉํ๊ธฐ ์ฌ์ด ์ธํฐํ์ด์ค๋ฅผ ์ ๊ณตํฉ๋๋ค.
- I/O ๋ฐ์ด๋ ์ฑ๋ฅ: ๋คํธ์ํฌ ์์ฒญ, ํ์ผ ์ฝ๊ธฐ ๋๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฟผ๋ฆฌ์ ๊ฐ์ด ์๋นํ ์๊ฐ์ I/O ์์ ๋๊ธฐ์ ์๋นํ๋ ์์ ์ ํ์ํฉ๋๋ค.
- ๋ฎ์ ์ค๋ฒํค๋: ์ค๋ ๋๋ ์ผ๋ฐ์ ์ผ๋ก ํ๋ก์ธ์ค์ ๋นํด ์ค๋ฒํค๋๊ฐ ๋ฎ์ ์ฆ์ ์ปจํ ์คํธ ์ ํ์ด ํฌํจ๋ ์์ ์ ๋ ํจ์จ์ ์ ๋๋ค.
ThreadPoolExecutor์ ํ๊ณ
- GIL ์ ํ: GIL์ CPU ๋ฐ์ด๋ ์์ ์ ๋ํ ์ง์ ํ ๋ณ๋ ฌ์ฑ์ ์ ํํฉ๋๋ค. ํ ๋ฒ์ ํ๋์ ์ค๋ ๋๋ง Python ๋ฐ์ดํธ์ฝ๋๋ฅผ ์คํํ ์ ์์ด ์ฌ๋ฌ ์ฝ์ด์ ์ด์ ์ ๋ฌดํจํํฉ๋๋ค.
- ๋๋ฒ๊น ๋ณต์ก์ฑ: ๋ฉํฐ์ค๋ ๋ ์ ํ๋ฆฌ์ผ์ด์ ๋๋ฒ๊น ์ ๋ ์ด์ค ์ปจ๋์ ๋ฐ ๊ธฐํ ๋์์ฑ ๊ด๋ จ ๋ฌธ์ ๋ก ์ธํด ์ด๋ ค์ธ ์ ์์ต๋๋ค.
ProcessPoolExecutor: CPU ๋ฐ์ด๋ ์์ ์ ์ํ ๋ฉํฐํ๋ก์ธ์ฑ ํ์ฉ
ProcessPoolExecutor
๋ ์์ปค ํ๋ก์ธ์ค ํ์ ์์ฑํ์ฌ GIL ์ ํ์ ๊ทน๋ณตํฉ๋๋ค. ๊ฐ ํ๋ก์ธ์ค๋ ์์ฒด Python ์ธํฐํ๋ฆฌํฐ์ ๋ฉ๋ชจ๋ฆฌ ๊ณต๊ฐ์ ๊ฐ์ง๋ฏ๋ก ๋ฉํฐ์ฝ์ด ์์คํ
์์ ์ง์ ํ ๋ณ๋ ฌ์ฑ์ ํ์ฉํฉ๋๋ค. ์ด๋ ๋ฌด๊ฑฐ์ด ๊ณ์ฐ์ด ํฌํจ๋ CPU ๋ฐ์ด๋ ์์
์ ์ด์์ ์
๋๋ค.
๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ
๋ง์ ์์ ์ซ์์ ๋ํ ์ ๊ณฑ ํฉ ๊ณ์ฐ๊ณผ ๊ฐ์ ๊ณ์ฐ ์ง์ฝ์ ์ธ ์์
์ ๊ณ ๋ คํด ๋ณด์ธ์. ProcessPoolExecutor
๋ฅผ ์ฌ์ฉํ์ฌ ์ด ์์
์ ๋ณ๋ ฌํํ๋ ๋ฐฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
import concurrent.futures
import time
import os
def sum_of_squares(start, end):
pid = os.getpid()
print(f"Process ID: {pid}, Calculating sum of squares from {start} to {end}")
total = 0
for i in range(start, end + 1):
total += i * i
return total
if __name__ == "__main__": # ์ผ๋ถ ํ๊ฒฝ์์ ์ฌ๊ท์ ์คํจ๋์ ํผํ๊ธฐ ์ํด ์ค์
start_time = time.time()
range_size = 1000000
num_processes = 4
ranges = [(i * range_size + 1, (i + 1) * range_size) for i in range(num_processes)]
with concurrent.futures.ProcessPoolExecutor(max_workers=num_processes) as executor:
futures = [executor.submit(sum_of_squares, start, end) for start, end in ranges]
results = [future.result() for future in concurrent.futures.as_completed(futures)]
total_sum = sum(results)
print(f"Total sum of squares: {total_sum}")
print(f"Time taken: {time.time() - start_time:.2f} seconds")
์ค๋ช :
- ์ฃผ์ด์ง ์ซ์ ๋ฒ์์ ๋ํ ์ ๊ณฑ ํฉ์ ๊ณ์ฐํ๋
sum_of_squares
ํจ์๋ฅผ ์ ์ํฉ๋๋ค. ๊ฐ ๋ฒ์๊ฐ ์ด๋ค ํ๋ก์ธ์ค์์ ์คํ๋๋์ง ํ์ธํ๊ธฐ ์ํดos.getpid()
๋ฅผ ํฌํจํฉ๋๋ค. - ๋ฒ์ ํฌ๊ธฐ์ ์ฌ์ฉํ ํ๋ก์ธ์ค ์๋ฅผ ์ ์ํฉ๋๋ค.
ranges
๋ชฉ๋ก์ ์ด ๊ณ์ฐ ๋ฒ์๋ฅผ ๊ฐ ํ๋ก์ธ์ค์ ๋ํด ํ๋์ฉ ๋ ์์ ์ฒญํฌ๋ก ๋๋๊ธฐ ์ํด ์์ฑ๋ฉ๋๋ค. - ์ง์ ๋ ์์ ์์ปค ํ๋ก์ธ์ค๋ก
ProcessPoolExecutor
๋ฅผ ์์ฑํฉ๋๋ค. executor.submit(sum_of_squares, start, end)
๋ฅผ ์ฌ์ฉํ์ฌ ๊ฐ ๋ฒ์๋ฅผ ์คํ๊ธฐ๋ก ์ ์ถํฉ๋๋ค.future.result()
๋ฅผ ์ฌ์ฉํ์ฌ ๊ฐ ๋ฏธ๋์์ ๊ฒฐ๊ณผ๋ฅผ ์์งํฉ๋๋ค.- ๋ชจ๋ ํ๋ก์ธ์ค์ ๊ฒฐ๊ณผ๋ฅผ ํฉ์ฐํ์ฌ ์ต์ข ์ด๊ณ๋ฅผ ์ป์ต๋๋ค.
์ค์ ์ฐธ๊ณ ์ฌํญ: ProcessPoolExecutor
๋ฅผ ์ฌ์ฉํ ๋, ํนํ Windows์์๋ ์คํ๊ธฐ๋ฅผ ์์ฑํ๋ ์ฝ๋๋ฅผ if __name__ == "__main__":
๋ธ๋ก์ผ๋ก ๋ฌถ์ด์ผ ํฉ๋๋ค. ์ด๋ ํ๋ก์ธ์ค๊ฐ ์ฌ๊ท์ ์ผ๋ก ์คํจ๋๋๋ ๊ฒ์ ๋ฐฉ์งํ์ฌ ์ค๋ฅ์ ์์์น ๋ชปํ ๋์์ ์ ๋ฐํ ์ ์์ต๋๋ค. ์ด๋ ๋ชจ๋์ด ๊ฐ ํ์ ํ๋ก์ธ์ค์์ ๋ค์ ๊ฐ์ ธ์ค๊ธฐ ๋๋ฌธ์
๋๋ค.
ProcessPoolExecutor์ ์ด์
- ์ง์ ํ ๋ณ๋ ฌ์ฑ: GIL ์ ํ์ ๊ทน๋ณตํ์ฌ CPU ๋ฐ์ด๋ ์์ ์ ๋ํด ๋ฉํฐ์ฝ์ด ์์คํ ์์ ์ง์ ํ ๋ณ๋ ฌ์ฑ์ ํ์ฉํฉ๋๋ค.
- CPU ๋ฐ์ด๋ ์์ ์ ๋ํ ์ฑ๋ฅ ํฅ์: ๊ณ์ฐ ์ง์ฝ์ ์ธ ์์ ์ ๋ํด ์๋นํ ์ฑ๋ฅ ํฅ์์ ๋ฌ์ฑํ ์ ์์ต๋๋ค.
- ๊ฒฌ๊ณ ์ฑ: ํ ํ๋ก์ธ์ค๊ฐ ์ถฉ๋ํ๋๋ผ๋ ํ๋ก์ธ์ค๋ ์๋ก ๊ฒฉ๋ฆฌ๋์ด ์์ผ๋ฏ๋ก ์ ์ฒด ํ๋ก๊ทธ๋จ์ด ๋ค์ด๋์ง ์์ ์ ์์ต๋๋ค.
ProcessPoolExecutor์ ํ๊ณ
- ๋์ ์ค๋ฒํค๋: ํ๋ก์ธ์ค ์์ฑ ๋ฐ ๊ด๋ฆฌ๋ ์ค๋ ๋์ ๋นํด ์ค๋ฒํค๋๊ฐ ๋์ต๋๋ค.
- ํ๋ก์ธ์ค ๊ฐ ํต์ : ํ๋ก์ธ์ค ๊ฐ ๋ฐ์ดํฐ ๊ณต์ ๋ ๋ ๋ณต์กํ ์ ์์ผ๋ฉฐ ์ค๋ฒํค๋๋ฅผ ์ถ๊ฐํ ์ ์๋ ํ๋ก์ธ์ค ๊ฐ ํต์ (IPC) ๋ฉ์ปค๋์ฆ์ด ํ์ํฉ๋๋ค.
- ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋: ๊ฐ ํ๋ก์ธ์ค๋ ์์ฒด ๋ฉ๋ชจ๋ฆฌ ๊ณต๊ฐ์ ๊ฐ์ง๋ฏ๋ก ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ์ฒด ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ด ์ฆ๊ฐํ ์ ์์ต๋๋ค. ํ๋ก์ธ์ค ๊ฐ์ ๋ง์ ์์ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ๋ ๊ฒ์ ๋ณ๋ชฉ ํ์์ด ๋ ์ ์์ต๋๋ค.
์ฌ๋ฐ๋ฅธ ์คํ๊ธฐ ์ ํ: ThreadPoolExecutor vs ProcessPoolExecutor
ThreadPoolExecutor
์ ProcessPoolExecutor
์ค์์ ์ ํํ๋ ์ด์ ๋ ์์
์ ์ฑ๊ฒฉ์ ์ดํดํ๋ ๋ฐ ์์ต๋๋ค.
- I/O ๋ฐ์ด๋ ์์
: ์์
์ด I/O ์์
(์: ๋คํธ์ํฌ ์์ฒญ, ํ์ผ ์ฝ๊ธฐ, ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฟผ๋ฆฌ)์ ๊ธฐ๋ค๋ฆฌ๋ ๋ฐ ๋๋ถ๋ถ์ ์๊ฐ์ ์๋นํ๋ ๊ฒฝ์ฐ
ThreadPoolExecutor
๊ฐ ์ผ๋ฐ์ ์ผ๋ก ๋ ๋์ ์ ํ์ ๋๋ค. ์ด๋ฌํ ์๋๋ฆฌ์ค์์๋ GIL์ด ๋ณ๋ชฉ ํ์์ด ๋ํ๋ฉฐ ์ค๋ ๋์ ๋ฎ์ ์ค๋ฒํค๋๊ฐ ๋ ํจ์จ์ ์ ๋๋ค. - CPU ๋ฐ์ด๋ ์์
: ์์
์ด ๊ณ์ฐ ์ง์ฝ์ ์ด๊ณ ์ฌ๋ฌ ์ฝ์ด๋ฅผ ํ์ฉํ๋ ๊ฒฝ์ฐ
ProcessPoolExecutor
๊ฐ ์ ๋ต์ ๋๋ค. GIL ์ ํ์ ์ฐํํ๊ณ ์ง์ ํ ๋ณ๋ ฌ์ฑ์ ํ์ฉํ์ฌ ์๋นํ ์ฑ๋ฅ ํฅ์์ ๊ฐ์ ธ์ต๋๋ค.
๋ค์ ํ๋ ์ฃผ์ ์ฐจ์ด์ ์ ์์ฝํ ๊ฒ์ ๋๋ค.
๊ธฐ๋ฅ | ThreadPoolExecutor | ProcessPoolExecutor |
---|---|---|
๋์์ฑ ๋ชจ๋ธ | ๋ฉํฐ์ค๋ ๋ฉ | ๋ฉํฐํ๋ก์ธ์ฑ |
GIL ์ํฅ | GIL์ ์ํด ์ ํ๋จ | GIL ์ฐํ |
์ ํฉํ ์์ | I/O ๋ฐ์ด๋ ์์ | CPU ๋ฐ์ด๋ ์์ |
์ค๋ฒํค๋ | ๋ฎ์ | ๋์ |
๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ | ๋ฎ์ | ๋์ |
ํ๋ก์ธ์ค ๊ฐ ํต์ | ํ์ ์์ (์ค๋ ๋๋ ๋ฉ๋ชจ๋ฆฌ ๊ณต์ ) | ๋ฐ์ดํฐ ๊ณต์ ์ ํ์ |
๊ฒฌ๊ณ ์ฑ | ๋ ๊ฒฌ๊ณ ํจ (์ถฉ๋ ์ ์ ์ฒด ํ๋ก์ธ์ค์ ์ํฅ) | ๋ ๊ฒฌ๊ณ ํจ (ํ๋ก์ธ์ค ๊ฒฉ๋ฆฌ) |
๊ณ ๊ธ ๊ธฐ๋ฒ ๋ฐ ๊ณ ๋ ค ์ฌํญ
์ธ์์ ํจ๊ป ์์ ์ ์ถ
๋ ์คํ๊ธฐ ๋ชจ๋ ์คํ ์ค์ธ ํจ์์ ์ธ์๋ฅผ ์ ๋ฌํ ์ ์์ต๋๋ค. ์ด๋ submit()
๋ฉ์๋๋ฅผ ํตํด ์ํ๋ฉ๋๋ค.
with concurrent.futures.ThreadPoolExecutor() as executor:
future = executor.submit(my_function, arg1, arg2)
result = future.result()
์์ธ ์ฒ๋ฆฌ
์คํ๋ ํจ์ ๋ด์์ ๋ฐ์ํ๋ ์์ธ๋ ๋ฉ์ธ ์ค๋ ๋ ๋๋ ํ๋ก์ธ์ค๋ก ์๋์ผ๋ก ์ ํ๋์ง ์์ต๋๋ค. Future
๊ฒฐ๊ณผ๋ฅผ ๊ฒ์ํ ๋ ๋ช
์์ ์ผ๋ก ์ฒ๋ฆฌํด์ผ ํฉ๋๋ค.
with concurrent.futures.ThreadPoolExecutor() as executor:
future = executor.submit(my_function)
try:
result = future.result()
except Exception as e:
print(f"An exception occurred: {e}")
๋จ์ ์์ ์ `map` ์ฌ์ฉ
์
๋ ฅ ์ํ์ค์ ๋์ผํ ํจ์๋ฅผ ์ ์ฉํ๋ ค๋ ๊ฐ๋จํ ์์
์ ๊ฒฝ์ฐ, map()
๋ฉ์๋๋ ์์
์ ์ ์ถํ๋ ๊ฐ๊ฒฐํ ๋ฐฉ๋ฒ์ ์ ๊ณตํฉ๋๋ค.
def square(x):
return x * x
with concurrent.futures.ProcessPoolExecutor() as executor:
numbers = [1, 2, 3, 4, 5]
results = executor.map(square, numbers)
print(list(results))
์์ ์ ์ ์ ์ด
ThreadPoolExecutor
๋ฐ ProcessPoolExecutor
๋ชจ๋์ max_workers
์ธ์๋ ๋์์ ์ฌ์ฉํ ์ ์๋ ์ต๋ ์ค๋ ๋ ๋๋ ํ๋ก์ธ์ค ์๋ฅผ ์ ์ดํฉ๋๋ค. max_workers
์ ๋ํ ์ฌ๋ฐ๋ฅธ ๊ฐ์ ์ ํํ๋ ๊ฒ์ ์ฑ๋ฅ์ ์ค์ํฉ๋๋ค. ์ข์ ์์์ ์ ์์คํ
์์ ์ฌ์ฉ ๊ฐ๋ฅํ CPU ์ฝ์ด ์์
๋๋ค. ๊ทธ๋ฌ๋ I/O ๋ฐ์ด๋ ์์
์ ๊ฒฝ์ฐ ์ค๋ ๋๊ฐ I/O๋ฅผ ๊ธฐ๋ค๋ฆฌ๋ ๋์ ๋ค๋ฅธ ์์
์ผ๋ก ์ ํ๋ ์ ์์ผ๋ฏ๋ก ์ฝ์ด๋ณด๋ค ๋ ๋ง์ ์ค๋ ๋๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ ๋ฆฌํ ์ ์์ต๋๋ค. ํน์ ์ฌ์ฉ ์ฌ๋ก์ ๋ํ ์ต์ ๊ฐ์ ๊ฒฐ์ ํ๋ ค๋ฉด ์คํ ๋ฐ ํ๋กํ์ผ๋ง์ด ์ข
์ข
ํ์ํฉ๋๋ค.
์งํ ์ํฉ ๋ชจ๋ํฐ๋ง
concurrent.futures
๋ชจ๋์ ์์
์งํ ์ํฉ์ ์ง์ ๋ชจ๋ํฐ๋งํ๊ธฐ ์ํ ๋ด์ฅ ๋ฉ์ปค๋์ฆ์ ์ ๊ณตํ์ง ์์ต๋๋ค. ๊ทธ๋ฌ๋ ์ฝ๋ฐฑ์ด๋ ๊ณต์ ๋ณ์๋ฅผ ์ฌ์ฉํ์ฌ ์์ฒด ์งํ ์ํฉ ์ถ์ ์ ๊ตฌํํ ์ ์์ต๋๋ค. `tqdm`๊ณผ ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์งํ๋ฅ ํ์์ค์ ํ์ํ๋๋ก ํตํฉ๋ ์ ์์ต๋๋ค.
์ค์ ์์
ThreadPoolExecutor
์ ProcessPoolExecutor
๋ฅผ ํจ๊ณผ์ ์ผ๋ก ์ ์ฉํ ์ ์๋ ๋ช ๊ฐ์ง ์ค์ ์๋๋ฆฌ์ค๋ฅผ ๊ณ ๋ คํด ๋ณด๊ฒ ์ต๋๋ค.
- ์น ์คํฌ๋ํ:
ThreadPoolExecutor
๋ฅผ ์ฌ์ฉํ์ฌ ์ฌ๋ฌ ์น ํ์ด์ง๋ฅผ ๋์์ ์ผ๋ก ๋ค์ด๋ก๋ํ๊ณ ๊ตฌ๋ฌธ ๋ถ์ํฉ๋๋ค. ๊ฐ ์ค๋ ๋๋ ๋ค๋ฅธ ์น ํ์ด์ง๋ฅผ ์ฒ๋ฆฌํ์ฌ ์ ์ฒด ์คํฌ๋ํ ์๋๋ฅผ ํฅ์์ํฌ ์ ์์ต๋๋ค. ์น์ฌ์ดํธ ์ด์ฉ ์ฝ๊ด์ ์ฃผ์ํ๊ณ ์๋ฒ์ ๊ณผ๋ถํ๋ฅผ ์ฃผ์ง ์๋๋ก ์ฃผ์ํ์ธ์. - ์ด๋ฏธ์ง ์ฒ๋ฆฌ:
ProcessPoolExecutor
๋ฅผ ์ฌ์ฉํ์ฌ ๋๋์ ์ด๋ฏธ์ง์ ์ด๋ฏธ์ง ํํฐ ๋๋ ๋ณํ์ ์ ์ฉํฉ๋๋ค. ๊ฐ ํ๋ก์ธ์ค๋ ๋ค๋ฅธ ์ด๋ฏธ์ง๋ฅผ ์ฒ๋ฆฌํ์ฌ ์ฌ๋ฌ ์ฝ์ด๋ฅผ ํ์ฉํ์ฌ ๋ ๋น ๋ฅธ ์ฒ๋ฆฌ๋ฅผ ํ ์ ์์ต๋๋ค. ํจ์จ์ ์ธ ์ด๋ฏธ์ง ์ฒ๋ฆฌ๋ฅผ ์ํด OpenCV์ ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๊ณ ๋ คํ์ธ์. - ๋ฐ์ดํฐ ๋ถ์:
ProcessPoolExecutor
๋ฅผ ์ฌ์ฉํ์ฌ ๋๊ท๋ชจ ๋ฐ์ดํฐ ์ธํธ์ ๋ํ ๋ณต์กํ ๊ณ์ฐ์ ์ํํฉ๋๋ค. ๊ฐ ํ๋ก์ธ์ค๋ ๋ฐ์ดํฐ์ ํ์ ์งํฉ์ ๋ถ์ํ์ฌ ์ ์ฒด ๋ถ์ ์๊ฐ์ ๋จ์ถํ ์ ์์ต๋๋ค. Pandas ๋ฐ NumPy๋ Python์์ ๋ฐ์ดํฐ ๋ถ์์ ๋๋ฆฌ ์ฌ์ฉ๋๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค. - ๊ธฐ๊ณ ํ์ต:
ProcessPoolExecutor
๋ฅผ ์ฌ์ฉํ์ฌ ๊ธฐ๊ณ ํ์ต ๋ชจ๋ธ์ ํ๋ จํฉ๋๋ค. ์ผ๋ถ ๊ธฐ๊ณ ํ์ต ์๊ณ ๋ฆฌ์ฆ์ ํจ๊ณผ์ ์ผ๋ก ๋ณ๋ ฌํ๋์ด ๋ ๋น ๋ฅธ ํ๋ จ ์๊ฐ์ ํ์ฉํ ์ ์์ต๋๋ค. scikit-learn ๋ฐ TensorFlow์ ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๋ณ๋ ฌํ๋ฅผ ์ง์ํฉ๋๋ค. - ๋น๋์ค ์ธ์ฝ๋ฉ:
ProcessPoolExecutor
๋ฅผ ์ฌ์ฉํ์ฌ ๋น๋์ค ํ์ผ์ ๋ค๋ฅธ ํ์์ผ๋ก ๋ณํํฉ๋๋ค. ๊ฐ ํ๋ก์ธ์ค๋ ๋ค๋ฅธ ๋น๋์ค ์ธ๊ทธ๋จผํธ๋ฅผ ์ธ์ฝ๋ฉํ์ฌ ์ ์ฒด ์ธ์ฝ๋ฉ ํ๋ก์ธ์ค๋ฅผ ๋ ๋น ๋ฅด๊ฒ ๋ง๋ค ์ ์์ต๋๋ค.
๊ธ๋ก๋ฒ ๊ณ ๋ ค ์ฌํญ
์ ์ธ๊ณ์ ์ธ ์ฒญ์ค์ ์ํ ๋์ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฐ๋ฐํ ๋๋ ๋ค์ ์ฌํญ์ ๊ณ ๋ คํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค.
- ์๊ฐ๋: ์๊ฐ ๊ด๋ จ ์์ ์ ๋ค๋ฃฐ ๋ ์๊ฐ๋๋ฅผ ์ผ๋์ ๋์ญ์์ค. `pytz`์ ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ ์๊ฐ๋ ๋ณํ์ ์ฒ๋ฆฌํฉ๋๋ค.
- ๋ก์บ: ์ ํ๋ฆฌ์ผ์ด์ ์ด ๋ค๋ฅธ ๋ก์บ์ ์ฌ๋ฐ๋ฅด๊ฒ ์ฒ๋ฆฌํ๋์ง ํ์ธํฉ๋๋ค. `locale`๊ณผ ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ ์ฌ์ฉ์์ ๋ก์บ์ ๋ฐ๋ผ ์ซ์, ๋ ์ง ๋ฐ ํตํ๋ฅผ ํ์ํํฉ๋๋ค.
- ๋ฌธ์ ์ธ์ฝ๋ฉ: ๋ค์ํ ์ธ์ด๋ฅผ ์ง์ํ๊ธฐ ์ํด ๊ธฐ๋ณธ ๋ฌธ์ ์ธ์ฝ๋ฉ์ผ๋ก ์ ๋์ฝ๋(UTF-8)๋ฅผ ์ฌ์ฉํฉ๋๋ค.
- ๊ตญ์ ํ(i18n) ๋ฐ ํ์งํ(l10n): ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฝ๊ฒ ๊ตญ์ ํ ๋ฐ ํ์งํํ ์ ์๋๋ก ์ค๊ณํฉ๋๋ค. gettext ๋๋ ๊ธฐํ ๋ฒ์ญ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ ๋ค๋ฅธ ์ธ์ด์ ๋ํ ๋ฒ์ญ์ ์ ๊ณตํฉ๋๋ค.
- ๋คํธ์ํฌ ์ง์ฐ ์๊ฐ: ์๊ฒฉ ์๋น์ค์ ํต์ ํ ๋ ๋คํธ์ํฌ ์ง์ฐ ์๊ฐ์ ๊ณ ๋ คํ์ญ์์ค. ์ ํ๋ฆฌ์ผ์ด์ ์ด ๋คํธ์ํฌ ๋ฌธ์ ์ ๋ณต์๋ ฅ์ด ์๋์ง ํ์ธํ๊ธฐ ์ํด ์ ์ ํ ํ์์์ ๋ฐ ์ค๋ฅ ์ฒ๋ฆฌ๋ฅผ ๊ตฌํํ์ญ์์ค. ์๋ฒ์ ์ง๋ฆฌ์ ์์น๋ ์ง์ฐ ์๊ฐ์ ์๋นํ ์ํฅ์ ๋ฏธ์น ์ ์์ต๋๋ค. ๋ค๋ฅธ ์ง์ญ์ ์ฌ์ฉ์์๊ฒ ์ฑ๋ฅ์ ํฅ์์ํค๊ธฐ ์ํด ์ฝํ ์ธ ์ ์ก ๋คํธ์ํฌ(CDN) ์ฌ์ฉ์ ๊ณ ๋ คํ์ญ์์ค.
๊ฒฐ๋ก
concurrent.futures
๋ชจ๋์ Python ์ ํ๋ฆฌ์ผ์ด์
์ ๋์์ฑ๊ณผ ๋ณ๋ ฌ์ฑ์ ๋์
ํ ์ ์๋ ๊ฐ๋ ฅํ๊ณ ํธ๋ฆฌํ ๋ฐฉ๋ฒ์ ์ ๊ณตํฉ๋๋ค. ThreadPoolExecutor
์ ProcessPoolExecutor
๊ฐ์ ์ฐจ์ด์ ์ ์ดํดํ๊ณ ์์
์ ์ฑ๊ฒฉ์ ์ ์คํ๊ฒ ๊ณ ๋ คํจ์ผ๋ก์จ ์ฝ๋์ ์ฑ๋ฅ๊ณผ ์๋ต์ฑ์ ํฌ๊ฒ ํฅ์์ํฌ ์ ์์ต๋๋ค. ํน์ ์ฌ์ฉ ์ฌ๋ก์ ๋ํ ์ต์ ์ค์ ์ ์ฐพ๊ธฐ ์ํด ์ฝ๋๋ฅผ ํ๋กํ์ผ๋งํ๊ณ ๋ค์ํ ๊ตฌ์ฑ์ ์คํํ๋ ๊ฒ์ ์์ง ๋ง์ญ์์ค. ๋ํ GIL์ ํ๊ณ์ ๋ฉํฐ์ค๋ ๋ฉ ๋ฐ ๋ฉํฐํ๋ก์ธ์ฑ ํ๋ก๊ทธ๋๋ฐ์ ์ ์ฌ์ ์ธ ๋ณต์ก์ฑ์ ์ ์ํ์ญ์์ค. ์ ์คํ ๊ณํ๊ณผ ๊ตฌํ์ ํตํด Python์์ ๋์์ฑ์ ์ ์ฌ๋ ฅ์ ์ต๋ํ ๋ฐํํ๊ณ ์ ์ธ๊ณ์ ์ธ ์ฒญ์ค์ ์ํ ๊ฒฌ๊ณ ํ๊ณ ํ์ฅ ๊ฐ๋ฅํ ์ ํ๋ฆฌ์ผ์ด์
์ ๋ง๋ค ์ ์์ต๋๋ค.