asyncio로 비동기 처리하여 병렬처리 속도 높이기

반응형

파이썬은 기본적으로 동기 방식으로 동작하는 언어이지만, 파이썬 3.4에서 asyncio가 표준 라이브러리로 추가되고,

파이썬 3.5에서부터는 async/await 키워드가 문법으로 채택이 되면서, 파이썬도 이제 언어 자체적으로 비동기 프로그래밍이 가능해 졌다.

 

예를 들어, 다음과 같이 선언된 함수는 동기 함수입니다.

def do_sync(): 
     pass

기존 def 키워드 앞에 async 키워드까지 붙이면 이 함수는 비동기 처리되며, 이러한 비동기 함수를 파이썬에서는 코루틴(coroutine)이라고도 부릅니다.

async def do_async(): 
      pass

이러한 비동기 함수는 일반 동기 함수가 호출하듯이 호출하면 coroutine 객체가 리턴됩니다.

do_async() # <coroutine object do_async at 0x1038de710>

따라서 비동기 함수는 일반적으로 async로 선언된 다른 비동기 함수 내에서 await 키워드를 붙여서 호출해야 합니다.

async def main_async(): 
     await do_async()

async로 선언되지 않은 일반 동기 함수 내에서 비동기 함수를 호출하려면 asyncio 라이브러리의 이벤트 루프를 이용해야합니다.

loop = asyncio.get_event_loop() 
loop.run_until_complete(main_async()) 
loop.close()

파이썬 3.7 이상에서는 다음과 같이 한 줄로 간단히 비동기 함수를 호출 할 수도 있습니다.

asyncio.run(main_async())

 

실제 저의 프로그램에 구현하기(goldstock 프로그램 일부 발췌)

import asyncio

class Kiwoom(QAxWidget):
     def __init__(self):
          super().__init__()
          self.ui = SetupUI()
          self.realType = RealType()

          self.logging = Logging()
          self.slack = Slack() # 슬랙 동작

          #중간생략...

    def auto_AI_recommend_clicked(self):
          now = datetime.now()
          if self.ui.btnCdtResult_buy.isChecked():
               self.ai_recommend_timer_start()
          else:
               self.timer_ai_recommend.stop()
          return


     def ai_recommend_timer_start(self):
          self.timer_ai_recommend = QTimer()
          self.timer_ai_recommend.start(5 * 1000) # 5초 간격으로 
          self.timer_ai_recommend.timeout.connect(self.ai_recommend_click)

     def ai_recommend_click(self):

          market_status, decision_list = self.decide_to_autoTrading(stockCode)

          if market_status == "매수" or market_status == "강력매수":
               if self.ui.btnAutotrade.isChecked():
                    asyncio.run(self.decide_to_sell(1, stockCode))
                    asyncio.run(self.decide_to_sell(2, balance_detail_stockCode))

          else:
               if (market_status == "매도" or market_status == "강력매도") and stockCode in self.account_stock_dict:

                    if self.ui.btnAutotrade.isChecked():
                         asyncio.run(self.decide_to_sell(1, stockCode))
                         asyncio.run(self.decide_to_sell(2, balance_detail_stockCode))


     async def decide_to_sell(self, num, stockCode): 
          day_chart_data = {}
          minute_chart_data = {}
          week_chart_data = {}
          month_chart_data = {}
          recommInterval = str(self.ui.cbRecommInterval.currentText().replace(' ', ''))
          minute_chart_interval = str(self.ui.spinBox_minutes.value())


          market_status, decision_list = self.decide_to_autoTrading_condtion2(stockCode=stockCode)
          if market_status in ("매도", "강력매도"):
              self.time_to_trading(order_currency=stockCode, type="매도", market_status="매도", \
                    current_price=current_price, target_time=recommInterval, date=order_date, real_flag="R", \
                    decision_list=decision_list)

          elif market_status in ("매수", "강력매수"):
              self.time_to_trading(order_currency=stockCode, type="매수", market_status="매수", \
                    current_price=current_price, target_time=recommInterval, date=order_date, real_flag="R", \
                    decision_list=decision_list)

          else: # 관망
               pass
     def time_to_trading(self, order_currency="", type="", market_status="", target_units=0, current_price=0, \
               target_time="", date="", real_flag="", decision_list=""):
          if type in ("매도", "강력매도"): # 매도
               try:
                    self.sendOrder("신규매도", order_currency, "시장가", int(possible_quantity), int(current_price))
               except (ParameterTypeError, KiwoomProcessingError) as e:
                    self.showDialog('Critical', e)
                    return
               self.slack.notification(\
                    pretext="%s(%s) 시장가 매도주문 완료(로직No:%s)" % (stockName, order_currency, str_decision_list),
                    title="%s 주문수량: %s 현재가: %s" % (self.now.strftime("%Y-%m-%d %H:%M:%S"), possible_quantity, \
                            current_price),
                    text="https://finance.daum.net/chart/A%s" % order_currency)
          else:
               #매수주문
               try:
                    self.ui.pteLog.appendPlainText("%s(%s) (신규매수,시장가)주문전송" % (stockName, order_currency))
                    self.logging.logger.debug("%s(%s) (신규매수,시장가)주문전송" % (stockName, order_currency))
                    self.sendOrder("신규매수", order_currency, "시장가", int(target_units), int(current_price))
              except (ParameterTypeError, KiwoomProcessingError) as e:
                    self.showDialog('Critical', e)
                    self.ui.pteLog.appendPlainText("%s(%s) 매수주문 오류발생" % (stockName, order_currency))
                    self.logging.logger.debug("%s(%s) 매수주문 오류발생" % (stockName, order_currency))
                    return
          # 거래내역 DB저장
          self.insert_db_transaction_infos(self.not_account_stock_dict[self.orderNo]) # result

이 프로그램에 실제 구현하여 동시성 병렬프로그램 함수 처리속도가 배가 빨라졌습니다.

 

이 기능에 대한 설명은 하기 사이트를 참조하였습니다. 감사합니다.

https://www.daleseo.com/python-asyncio/

 

[파이썬] asyncio로 비동기 처리하기

Engineering Blog by Dale Seo

www.daleseo.com

 

반응형

댓글()