์ฝ๋ - ํจ์บ ์์ ์ฝ๋ ์ฐธ๊ณ (ํจ์บ ์์ ์ ๋ฆฌ)
<์ด์ ๊ธ>
https://silvercoding.tistory.com/31
[python ์ฌํ] 14. Future ๋์์ฑ/ ๋น๋๊ธฐ ์์ ์คํ/ ThreadPoolExecutor, ProcessPoolExecutor
์ฝ๋ - ํจ์บ ์์ ์ฝ๋ ์ฐธ๊ณ (ํจ์บ ์์ ์ ๋ฆฌ) <์ด์ ๊ธ> https://silvercoding.tistory.com/30 https://silvercoding.tistory.com/29 https://silvercoding.tistory.com/28 https://silvercoding.tistory.com/27..
silvercoding.tistory.com
< Asyncio ์ฐธ๊ณ ๋ฌธ์ >
https://docs.python.org/ko/3/library/asyncio.html
asyncio — ๋น๋๊ธฐ I/O — Python 3.9.6 ๋ฌธ์
docs.python.org
- Asyncio
: ๋น๋๊ธฐ I/O Coroutine ์์ ( *๋๊ธฐ: ๊ธฐ๋ค๋ ค์ผ ํ๋ ๊ฒ , ๋น๋๊ธฐ: ๊ธฐ๋ค๋ฆฌ์ง ์๊ณ ๋ฐ๋ก ์คํ๋๋ ๊ฒ)
: Generator : ๋ฐ๋ณต์ ์ธ ๊ฐ์ฒด Return (yield) , ์ฆ, ์คํํ๊ณ stop -> ๋ค๋ฅธ ์์ ์ผ๋ก์ ์์ -> stop ์ง์ ๋ถํฐ ์ฌ ์คํํ๋ ์๋ฆฌ์ด๋ค.
: Non-Blocking ๋น๋๊ธฐ ์ฒ๋ฆฌ์ ์ ํฉํ๋ค.
* ๋ณธ ํฌ์คํ ์์ ์ค์ตํ ๋ฐฉ๋ฒ๋ค
1. ์์ฐจ์คํ
2. ์ฐ๋ ๋๋ง ์ฌ์ฉ
3. ์ฝ๋ฃจํด์์ ์ฐ๋ ๋ ์ฐํ ์ฌ์ฉ
- 1. BlockIO ์์ : ์์ฐจ์คํ
import timeit
from urllib.request import urlopen
ํ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ import ํ๊ธฐ
urls = ['http://daum.net', 'https://google.com', 'https://apple.com', 'https://tistory.com', 'https://github.com', 'https://naver.com']
์์ ์์ url์ openํด ๋ณด๋ ์ค์ต์ ํ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ url ๋ฆฌ์คํธ๋ฅผ ๋ง๋ค์ด ์ค๋ค. ์ด 6 ๊ฐ์ url์ ๋ฃ์ด ์ฃผ์๋ค.
start = timeit.default_timer()
์์ฐจ ์คํ์ ํ๊ธฐ ์ ์๊ฐ์ ๊ธฐ๋กํด ๋๋ค. ์ด๋ฒ์๋ timeit.default_timer()๋ฅผ ์ด์ฉํ์ฌ ์๊ฐ์ ์ธก์ ํ๋ค.
* ์์ฐจ ์คํ๋ถ
for url in urls:
print('Start', url)
# ์ค์ ์์ฒญ
text = urlopen(url)
# ์ค์ ๋ด์ฉ
# print('Contents', text.read())
print('Done', url)
์์ ๊ฐ์ด urlopen์ ์ฌ์ฉํ์ฌ url์ ์ค์ ์์ฒญ์ ํ๋ค.
* ์๊ฐ ์ธก์ : ์๋ฃ์๊ฐ - ์์์๊ฐ / ๊ฒฐ๊ณผ ์ถ๋ ฅ
duration = timeit.default_timer() - start
# ์ด ์คํ์๊ฐ
print('Total Time', duration)
Start http://daum.net
Done http://daum.net
Start https://google.com
Done https://google.com
Start https://apple.com
Done https://apple.com
Start https://tistory.com
Done https://tistory.com
Start https://github.com
Done https://github.com
Start https://naver.com
Done https://naver.com
Total Time 12.8696096
์์ฐจ์ ์ผ๋ก url๋ฆฌ์คํธ์ ์ ์๋ ์์๋๋ก url์์ฒญ์ด ๋ ๊ฒ์ ๋ณผ ์ ์๋ค. ์๊ฐ์ ์ด 13์ด ์ ๋๊ฐ ๊ฑธ๋ ธ๋ค.
*** BlockIO : Thread ์ฌ์ฉ
: ์ฐ๋ ๋ ๊ฐ์ ๋ฐ GIL ๋ฌธ์ ์ผ๋, ๊ณต์ ๋ฉ๋ชจ๋ฆฌ ๋ฌธ์ ํด๊ฒฐ (๋ฎคํ ์ค)
- 2. Thread๋ง ์ฌ์ฉ
* ํ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ import
import timeit
from urllib.request import urlopen
from concurrent.futures import ThreadPoolExecutor
import threading
* url ๋ฆฌ์คํธ ์์ฑ ๋ฐ ์๊ฐ ๊ธฐ๋ก
urls = ['http://daum.net', 'https://google.com', 'https://apple.com', 'https://tistory.com', 'https://github.com', 'https://naver.com']
start = timeit.default_timer()
* fetch ํจ์ ์ ์ธ
def fetch(url):
print('Thread Name : ', threading.current_thread().getName(), 'Start', url) # ํ์ฌ ์ฐ๋๋๊ฐ ๋ฌด์์ธ์ง ์๋ ค์ค
urlopen(url)
print('Thread Name : ', threading.current_thread().getName(), 'Done', url)
ํ์ฌ ์ฐ๋ ๋์ ์ด๋ฆ๊ณผ , ์์(start) ๊ณผ ์๋ฃ(Done)์ ๊ณผ์ ์ ์ถ๋ ฅํ๋๋ก ํ๋ fetch ํจ์๋ฅผ ์์ฑํ๋ค.
* main ํจ์ ์ ์ธ - fetchํจ์ ์ฌ์ฉ
def main():
with ThreadPoolExecutor(max_workers=10) as executor:
for url in urls:
executor.submit(fetch, url)
ThreadPoolExecutor๋ฅผ ์ฌ์ฉํ๊ณ , max_workers๋ 10์ผ๋ก ๋ฐ์์ค๋ค. ์ด์ for๋ฌธ์ ์ด์ฉํ์ฌ url์ ํ๊ฐ์ฉ ๋ฃ์ด์ submit์ผ๋ก fetchํจ์์ ์ ์ฉํด ์ค๋ค.
* ์คํ
if __name__ == '__main__':
# ํจ์ ์คํ
main()
# ์๋ฃ์๊ฐ - ์์์๊ฐ
duration = timeit.default_timer() - start
# ์ด ์คํ์๊ฐ
print('Total Time', duration)
Thread Name : ThreadPoolExecutor-0_0 Start http://daum.net
Thread Name : ThreadPoolExecutor-0_1 Start https://google.com
Thread Name : ThreadPoolExecutor-0_2 Start https://apple.com
Thread Name : ThreadPoolExecutor-0_3 Start https://tistory.com
Thread Name : ThreadPoolExecutor-0_4 Start https://github.com
Thread Name : ThreadPoolExecutor-0_5 Start https://naver.com
Thread Name : ThreadPoolExecutor-0_4 Done https://github.com
Thread Name : ThreadPoolExecutor-0_2 Done https://apple.com
Thread Name : ThreadPoolExecutor-0_5 Done https://naver.com
Thread Name : ThreadPoolExecutor-0_3 Done https://tistory.com
Thread Name : ThreadPoolExecutor-0_0 Done http://daum.net
Thread Name : ThreadPoolExecutor-0_1 Done https://google.com
Total Time 0.9581279
start๋ for๋ฌธ์ ๋ค์ด๊ฐ ์์๋๋ก ์ถ๋ ฅ์ด ๋์๋๋ฐ, Done์ ์์๊ฐ ์ผ์ ํ์ง ์์ ๊ฒ์ ๋ณผ ์ ์๋ค. submit ํจ์๋ ๋จผ์ ์ผ์ด ๋๋๋ ์์๋๋ก ์ค๋ ๊ฒ์ผ ๋ฟ, ์์์ ๋ํ ์ ๊ฒฝ์ ์ฐ์ง ์๊ธฐ ๋๋ฌธ์ด๋ค. ์๊ฐ์ 1์ด๋ ๋์ง ์๋๋ค! ๊ต์ฅํ ์ค์ด ๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
- 3. ์ฝ๋ฃจํด์์ ์ฐ๋ ๋ ์ฐํ ์ฌ์ฉ
<์ฐธ๊ณ >
https://docs.python.org/ko/3/library/asyncio-task.html
์ฝ๋ฃจํด๊ณผ ํ์คํฌ — Python 3.9.6 ๋ฌธ์
์ฝ๋ฃจํด๊ณผ ํ์คํฌ ์ด ์ ์์๋ ์ฝ๋ฃจํด๊ณผ ํ์คํฌ๋ก ์์ ํ๊ธฐ ์ํ ๊ณ ๊ธ asyncio API์ ๊ดํด ์ค๋ช ํฉ๋๋ค. async/await ๋ฌธ๋ฒ์ผ๋ก ์ ์ธ๋ ์ฝ๋ฃจํด์ asyncio ์์ฉ ํ๋ก๊ทธ๋จ์ ์์ฑํ๋ ๊ธฐ๋ณธ ๋ฐฉ๋ฒ์ ๋๋ค. ์๋ฅผ
docs.python.org
*** ์ฝ๋ฃจํด์ ๋จ์ผ ์ฐ๋ ๋๋ผ๋ ๊ฒ์ด ํต์ฌ์ด๋ค. ํจ์ ์ฌ๋ฌ๊ฐ๋ฅผ ์์ฑํ์ฌ ๋ฉํฐ ์ฐ๋ ๋๋ฅผ ์ฌ์ฉํ๋ ๋ฏ ๋ณด์ด๊ฒ ํด์ค๋ค. ๋จ์ผ ์ฐ๋ ๋์์ ์ต์ ์ ์ฑ๋ฅ์ ์ด๋์ด๋ด๊ธฐ ์ํด ์ ์ด๋ฅผ ์๋ณดํ์ฌ ์ฌ์ฉํ๋ฉฐ, ๋น ๋ฅด๊ฒ ๋์์ฑ ๊ธฐ๋ฒ์ ํ์ฉํ๋ ํจํด์ด๋ค.
* ํ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ import
# aiohttp ์ฌ์ฉ ๊ฐ๋ฅ (Asyncio ์ง์)
from concurrent import futures
import timeit
from urllib.request import urlopen
from concurrent.futures import ThreadPoolExecutor
import threading
import asyncio
* url ๋ฆฌ์คํธ ์์ฑ ๋ฐ ์๊ฐ ๊ธฐ๋ก
urls = ['http://daum.net', 'https://google.com', 'https://apple.com', 'https://tistory.com', 'https://github.com', 'https://naver.com']
start = timeit.default_timer()
* fetch ํจ์ ์ ์ธ
async def fetch(url, executor):
print('Thread Name : ', threading.current_thread().getName(), 'Start', url) # ํ์ฌ ์ฐ๋๋๊ฐ ๋ฌด์์ธ์ง ์๋ ค์ค
res = await loop.run_in_executor(executor, urlopen, url) # (์ผ๊พผ, ์ผ๊พผ์ด ์ฒ๋ฆฌํ ํจ์, ํด์ผ๋ ์ผ (์ฌ์ดํธ)) / loop ์์๋ futures๊ฐ ๋ค์ด๊ฐ ์์ .
print('Thread Name : ', threading.current_thread().getName(), 'Done', url)
return res.read()[0:5]
https://silvercoding.tistory.com/30
[python ์ฌํ] 13. ์ฝ๋ฃจํด (coroutine) , yield from
์ฝ๋ - ํจ์บ ์์ ์ฝ๋ ์ฐธ๊ณ (ํจ์บ ์์ ์ ๋ฆฌ) <์ด์ ๊ธ> https://silvercoding.tistory.com/29 https://silvercoding.tistory.com/28 https://silvercoding.tistory.com/27 https://silvercoding.tistory.com/26..
silvercoding.tistory.com
์์ ํฌ์คํ ์์ yield from์ ๊ณต๋ถํ์๋ค. ๋ฒ์ 3.7? ์ด์ ๋ถํฐ await ์ผ๋ก ๋ฐ๋์๊ณ , ๋ณธ ํฌ์คํ ์์๋ await์ ์ฌ์ฉํ๋ค.
<loop ์ฐธ๊ณ ๋ฌธ์>
https://docs.python.org/ko/3/library/asyncio-eventloop.html
์ด๋ฒคํธ ๋ฃจํ — Python 3.9.6 ๋ฌธ์
์ด๋ฒคํธ ๋ฃจํ ์์ค ์ฝ๋: Lib/asyncio/events.py, Lib/asyncio/base_events.py ๋จธ๋ฆฌ๋ง ์ด๋ฒคํธ ๋ฃจํ๋ ๋ชจ๋ asyncio ์์ฉ ํ๋ก๊ทธ๋จ์ ํต์ฌ์ ๋๋ค. ์ด๋ฒคํธ ๋ฃจํ๋ ๋น๋๊ธฐ ํ์คํฌ ๋ฐ ์ฝ๋ฐฑ์ ์คํํ๊ณ ๋คํธ์ํฌ IO ์ฐ์ฐ
docs.python.org
* main ํจ์ ์ ์ธ
async def main():
# ์ฐ๋ ๋ ํ ์์ฑ
executor = ThreadPoolExecutor(max_workers=10)
# asyncio.ensure_future
futures = [asyncio.ensure_future(fetch(url, executor)) for url in urls]
# await = yield from
rst = await asyncio.gather(*futures) # unpacking
print()
print('Result : ', rst)
์ง๋ฅํ ๋ฆฌ์คํธ์ธ futures๋ฅผ ์ ์ธํ๋ค. asyncio.ensure_future์ fetchํจ์๋ฅผ ์ ์ฉํ๋ค.
* ๊ฒฐ๊ณผ ์คํ
if __name__ == '__main__':
# ๋ฃจํ ์์ฑ
loop = asyncio.get_event_loop()
# ๋ฃจํ ๋๊ธฐ
loop.run_until_complete(main())
# ํจ์ ์คํ
main()
# ์๋ฃ์๊ฐ - ์์์๊ฐ
duration = timeit.default_timer() - start
# ์ด ์คํ์๊ฐ
print('Total Time', duration)
Thread Name : MainThread Start http://daum.net
Thread Name : MainThread Start https://google.com
Thread Name : MainThread Start https://apple.com
Thread Name : MainThread Start https://tistory.com
Thread Name : MainThread Start https://github.com
Thread Name : MainThread Start https://naver.com
Thread Name : MainThread Done https://github.com
Thread Name : MainThread Done https://apple.com
Thread Name : MainThread Done https://naver.com
Thread Name : MainThread Done https://tistory.com
Thread Name : MainThread Done http://daum.net
Thread Name : MainThread Done https://google.com
Result : [b'<!DOC', b'<!doc', b'\n\t\n\n\n', b'\n\t<!d', b'\n\n\n\n\n', b'\n<!do']
c:/Users/./Desktop/python_high/chapter06_04_03.py:57: RuntimeWarning: coroutine 'main' was never awaited
main()
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
Total Time 1.3151724
๋ง์ฐฌ๊ฐ์ง๋ก ์์์ ์๊ด ์์ด ์ผ์ ํ๋ฉฐ, ์๊ฐ์ 2๋ฒ๋ฐฉ๋ฒ๋ณด๋ค๋ ์ค๋ ๊ฑธ๋ฆฌ์ง๋ง ๊ทธ๋๋ 1.3์ด๋ก ๋น ๋ฅด๊ฒ ์คํ๋ ๊ฒ์ ๋ณผ ์ ์๋ค.