참고 :
https://www.pythontutorial.net/python-concurrency/python-event-loop/
Python Event Loop
In this tutorial, you'll learn about the Python event loop and how Python uses it to achieve the concurrency model using a single thread.
www.pythontutorial.net
개요 :
Event Loop : 언제 쓰나? 왜 쓰나 ?
단일 스레드 동시성 모델을 달성하기 위해 asyncio패키지는 이벤트 루프라는 구성을 사용합니다.
파일에 데이터를 쓰는 예를 들어 이벤트 루프를 설명 하자면
1. 파일 열기
2. 파일에 데이터를 쓰고 완료될 때까지 기다립니다.
3. 파일 닫기
여기서 2번 째 작업이 BLOCKING 작업임 ( blocking 되면서 다른 작업을 할 수 없게 됨 )
2번째 작업을 자세히 설명 하자면
- 먼저 파일에 데이터를 쓰는 함수는 데이터를 운영체제(OS)로 보낸다.
- 둘째, OS가 인계받아 파일에 데이터 쓰기를 시작합니다.
- 셋째, OS는 파일 쓰기가 완료되면 프로그램에 알립니다. ( "event notification systems" 라는 이벤트 알림 시스템을 이용해서 프로그램에 알림 ) - OS 마다 이 시스템이 다르긴함. 대표적으로 리눅스는 epoll 방식을 사용
이런 상황에서 실제로 I/O 연산과 CPU 연산에 속도차이 때문에 Idle타임이 발생한다. ( CPU연산이 I/O연산을 기다리느라 아무것도 안하는 상태 )
I/O연산을 하는 걸 I/O 바운드 작업이라고 하는데 I/O바운드 작업이 발생 할 때 OS에서 미리 알려줌으로써 다른 코드를 실행할 수 있다. 그러면 I/O 연산은 미리 작업해둔 CPU 연산 결과를 받아서 idle 타임없이 돌아감.
idle 타임 감소 -> 연산속도 증가
그런데 이과정에서 필요한게 EVENT LOOP 이다.
미리 작업해둔 CPU 연산 결과를 Event Loop ( Queue 자료구조 ) 에 차곡차곡 넣어 놓고 하나씩 순서대로 I/O 연산작업을 한다.
그래서 코루틴에서 보면 'await', 즉 순서를 기다릴수 있는 작업이라고 표현하는 부분. // wait 라면 그냥 대기지만 await 는 Event Loop 에 있는 Task Queue( 작업대기열 ) 에 넣겠다는 의미이다. 동작 의미 X ! // 따라서 작업 대기열에 넣고 나서 "asyncio.run(main())" 함수로 Event Loop 를 동작 시켜줘야 awaitable 한 task 들이 동작 하는 거다
Event Loop : 작동원리
작동 원리
- 첫째, 메인 스레드는 태스크를 태스크 큐에 제출합니다.
- 둘째, 이벤트 루프는 지속적으로 작업 대기열을 모니터링하고 I/O 작업에 대응할 때까지 작업을 실행합니다. 이 경우 이벤트 루프는 작업을 일시 중지하고 OS에 전달합니다.
- 셋째, 완료된 IO 작업을 확인합니다. 작업이 완료되면 OS가 프로그램에 알립니다. 그런 다음 이벤트 루프는 일시 중지되지 않은 작업을 실행합니다.
작업 대기열이 비워질 때까지 이러한 단계가 반복됩니다.
참고:
https://www.educba.com/python-event-loop/
Python Event Loop | Complete Guide to Python Event Loop | Examples
This is a guide to Python Event Loop. Here we discuss an introduction, syntax, how does it work with examples to implement.
www.educba.com
예제 :
여기 확인점
asyncio.ensure_future vs. BaseEventLoop.create_task vs. simple coroutine?
I've seen several basic Python 3.5 tutorials on asyncio doing the same operation in various flavours. In this code: import asyncio async def doit(i): print("Start %d" % i) await asyncio...
stackoverflow.com
중요점만 정리해보자면 이렇다.
1.
get_event_loop() VS new_event_loop()
요건 루프개념을 좀 알아야댐.
main loop 를 쓰냐 아니면 loop를 새로 만들어서 사용하냐 차이 정도
2.
loop 로 다양하게 실행하는 방법들 여기선 4가지 다룸
try:
loop.call_soon(hi_everyone, loop) # hi_everyone 는 일반 함수이지만 loop 에 포함시켜서 동작시키는 방법
loop.run_forever()
finally:
loop.close()
------------------------------------------
try:
loop.run_until_complete(work()) # work() 는 async 함수 ( + loop.run_forever() 가 없어도 동작이 안끝남 )
finally:
loop.close()
------------------------------------------
try:
asyncio.ensure_future(work()) # work() 는 async 함수 ( + loop.run_forever() 가 없으면 바로 동작 끝나버림 )
loop.run_forever()
finally:
loop.close()
------------------------------------------
try:
loop.create_task(work()) # work() 는 async 함수 ( + loop.run_forever() 가 없으면 바로 동작 끝나버림 )
loop.run_forever()
finally:
loop.close()
------------------------------------------
3.
loop.call_soon 이랑 loop.run_until_complete 은 컨셉이 확실한데
loop.ensure_future 랑 loop.create_task 가 비슷함 그래서 비교 해봄
loop.ensure_future VS loop.create_task
타입 비교
task_1 = asyncio.create_task(firstWorker())
task_2 = asyncio.ensure_future(secondWorker())
# 둘다 async 함수를 인자로 넘기면 return 값도 동일하게 "<class '_asyncio.Task'>" 이다.
# 하지만 ensure_future에 Task or Future 가 아닌 다른 awaitable한 무언갈 넣으면 Task로 래핑되서 리턴해준다.
# ------------------------------------------------------------
def ex_1():
import time
import asyncio
def hi_everyone(loop):
print('Hi Everyone')
loop.stop()
loop = asyncio.get_event_loop()
loop.call_soon(hi_everyone, loop)
loop.run_forever()
loop.close()
"""
Hi Everyone
"""
# ------------------------------------------------------------
def ex_2():
import asyncio
## Define a coroutine that takes in a future
async def myCoroutine():
print("My Coroutine")
# 위에서 get 한 Event Loop 가 close 되지 않았으면 new_event_loop() 를 사용할 필요 없다.
# loop = asyncio.get_event_loop()
# 위에서 main thread 에서 default로 만든 event loop 를 close 했으므로 새로운 Event Loop 를 만들어 줘야 함.
loop = asyncio.new_event_loop()
try:
loop.run_until_complete(myCoroutine())
finally:
loop.close()
print('Event Loop를 close 합니다.')
"""
My Coroutine
Event Loop를 close 합니다.
"""
# ------------------------------------------------------------
def ex_3():
import asyncio
import time
async def myWork():
print("Starting Work")
time.sleep(5)
print("Finishing Work")
loop = asyncio.new_event_loop()
try:
loop.run_until_complete(myWork())
finally:
loop.close()
print('Event Loop를 close 합니다.')
"""
Starting Work
Finishing Work
"""
# ------------------------------------------------------------
def ex_4():
import asyncio
async def work():
while True:
await asyncio.sleep(1)
print("Task Executed")
loop = asyncio.get_event_loop()
try:
asyncio.ensure_future(work())
loop.run_forever()
except KeyboardInterrupt:
pass
finally:
print("Closing Loop")
loop.close()
"""
Task Executed
Task Executed
Task Executed
Task Executed
Task Executed
무한정 ~~
"""
# ------------------------------------------------------------
def ex_5():
import asyncio
import time
async def firstWorker():
while True:
await asyncio.sleep(1)
print("First Worker Executed")
async def secondWorker():
while True:
await asyncio.sleep(1)
print("Second Worker Executed")
loop = asyncio.get_event_loop()
try:
check_type_1 = asyncio.ensure_future(firstWorker())
asyncio.ensure_future(secondWorker())
print('type check ( ensure_future ) : ', type(check_type_1))
loop.run_forever()
print('loop_END')
except KeyboardInterrupt:
pass
finally:
print("Closing Loop")
loop.close()
"""
type check ( ensure_future ) : <class '_asyncio.Task'>
First Worker Executed
Second Worker Executed
First Worker Executed
Second Worker Executed
....... 무한정
"""
# ------------------------------------------------------------
def ex_6():
import asyncio
import time
async def firstWorker():
while True:
await asyncio.sleep(1)
print("First Worker Executed")
async def secondWorker():
while True:
await asyncio.sleep(1)
print("Second Worker Executed")
def thirdWorker():
while True:
time.sleep(1)
print("Third Worker Executed")
async def _run():
task_1 = asyncio.create_task(firstWorker())
task_2 = asyncio.ensure_future(secondWorker())
# 둘다 async 함수를 인자로 넘기면 return 값도 동일하게 "<class '_asyncio.Task'>" 이다.
print('type check ( task_1 ) : ', type(task_1))
print('type check ( task_2 ) : ', type(task_2))
# 하지만 ensure_future에 Task or Future 가 아닌 다른 awaitable한 무언갈 넣으면 Task로 래핑되서 리턴해준다.
await asyncio.gather(
task_1,
task_2
)
try:
loop = asyncio.get_event_loop()
loop.run_until_complete(_run())
except KeyboardInterrupt:
pass
finally:
print("Closing Loop")
loop.close()
"""
type check ( task_1 ) : <class '_asyncio.Task'>
type check ( task_2 ) : <class '_asyncio.Task'>
First Worker Executed
Second Worker Executed
First Worker Executed
Second Worker Executed
First Worker Executed
Second Worker Executed
....... 무한정
"""
if __name__=="__main__":
ex_6()
'언어 정리 > python_비동기관련_lib' 카테고리의 다른 글
agent,handler 모음 (ros2, gmqtt, asyncio.Queue) (1) | 2022.09.12 |
---|---|
asyncio Queue 랑 loop 개념까지 정리 (2) | 2022.09.06 |
다른사람 예제 rclpy.executors 랑 rclpy.task 관련 예시 퍼옴 (0) | 2022.08.17 |
이벤트 루프 - 파이썬 레퍼런스 정리 (0) | 2022.08.17 |
코루틴과 태스크, 퓨처 - 파이썬 레퍼런스 정리 (0) | 2022.08.17 |
댓글