์ฝ”๋“œ - ํŒจ์บ  ์ˆ˜์—… ์ฝ”๋“œ ์ฐธ๊ณ  (ํŒจ์บ  ์ˆ˜์—… ์ •๋ฆฌ)

 

 

<์ด์ „ ๊ธ€>

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์ดˆ๋กœ ๋น ๋ฅด๊ฒŒ ์‹คํ–‰๋œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. 

 

 

 

 

+ Recent posts