북치기_개발모드
북치기 개발모드
북치기_개발모드
전체 방문자
오늘
어제
  • 분류 전체보기 (39)
    • 개발 (28)
      • python (20)
      • flutter (0)
      • 잡종 (8)
    • 잡다한것들 (6)

블로그 메뉴

  • 홈
  • 태그
  • 미디어로그
  • 위치로그
  • 방명록

공지사항

인기 글

태그

  • 크롤링
  • 네이버
  • 셀레니움
  • 광주
  • 카멜리 파티룸
  • #공유오피스
  • 전대 파티룸
  • 파이썬
  • 사업자 전화번호
  • 브라이덜샤워
  • jsp
  • select
  • Update
  • mysql
  • 판매자db
  • 광주파티룸
  • delete
  • 마케팅db
  • Python
  • 카멜리파티룸

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
북치기_개발모드

북치기 개발모드

멀티프로세싱/멀티스레딩 df 중복 관련 정리
개발/python

멀티프로세싱/멀티스레딩 df 중복 관련 정리

2022. 12. 12. 18:58
반응형

이 말같지도 않은 코드를 기록하는 이유는 멍청한 실수를 방지하기 위함이다.

멀티~라는 것을 쓸 때에는 같은 자원을 참조하게끔 하면 예상치못한 결과가 발생하기 때문에

각자 자원을 쓸 수 있도록 장치가 필요하다.

 


멀티프로세싱으로 하면 df에 중복이 없고 멀티스레딩이나 그냥 for문으로 작동하면 중복이 생겼다.

중복제거를 하지않아도 중복이 없는 탓에 몇 시간 머리를 싸맸으나 테스트를 통해 이해를 하게되었다.

 

전역변수로 df 항목을 선언하였던게 문제였다.

멀티프로세싱으로 작업을 하게되면 같은 df를 쓰는게 아니라

프로세스 하나 마다 df를 새롭게 만들어서 사용하게 되는 것 같다.

따라서 이 df들을 concat해보면 중복이 없게된다.

아래는 테스트 코드이다.

import pandas as pd
from multiprocessing import Pool, freeze_support # Pool import하기

get_df = pd.DataFrame(columns=['순서'])

def test(number):
    print(f"----------{number}---------")
    for i in range(0,number):
        y = len(get_df)
        print(y)
        get_df.loc[y,"순서"]= i
    return get_df

if __name__ =="__main__":
    test_df=[0]
    freeze_support()
    get_list=[10,20,30]
    pool = Pool(processes = 4) 
    test_df[0] = pd.concat(pool.map(test,get_list))
    pool.close()                # 끝나면 닫아주고
    pool.join()                # 다른 프로세서들이 끝날 때까지 기다린다.

    print(test_df)

주의할 점은 빨리 끝나는 작업같은 경우에는 같은 df를 쓰게된다. 예시코드에서 숫자를 6, 7, 8 이런식으로 하면 같이쓴다.

즉 하나의 프로세스가 실행되고, 끝나기 전에 다른 프로세스가 실행된다면 중복되지않는다. 아마 보통의 작업은 이런식으로 될 것이다.

 

반면에 멀티임에도 하나의 프로세스가 끝난 뒤에 다른 프로세스가 실행되면 같이쓴다.

근본적으로는 df를 전역변수로 쓰지않아야한다는 것을 강조하고싶다.

 

멀티스레드는 중복 조차도 뒤죽박죽인 경우가 많다. 섬세하게 써야한다.

import asyncio
import requests
import time
import os
import threading
from concurrent.futures import ThreadPoolExecutor
import pandas as pd


get_df = pd.DataFrame(columns=['순서'])

def fetcher(url):

    i = f"{os.getpid()} process | {threading.get_ident()} url : {url}"
    y = len(get_df)
    get_df.loc[y,"순서"]= y
    return get_df

def main():
    urls=["https://www.naver.com","https://www.google.com","https://instagram.com"] * 50

    executor = ThreadPoolExecutor(max_workers=12)
    print(executor)

    params = [url for url in urls]
    test_df = pd.concat(list(executor.map(fetcher,params)))
    
    time.sleep(2)
    test_df.to_excel("123.xlsx",encoding="utf-8-sig",index=False)
        # print(result)

if __name__ == "__main__":
    # asyncio.run(main())
    start = time.time()
    main()
    end = time.time()
    print(end-start)

같은 코드임에도 이런 결과값이 나온다.

물론 당연한게 같은 자원(get_df)를 끌어쓰기 때문이긴하다만.


어느 영역에서도 똑같지만, 실력이 그렇게 높지않다면 데이터를 100%신뢰할 수 있는가에 대해서는 생각을 해봐야한다.

 

아래 코드처럼 df를 각 함수마다 구현한다면 일정한 결과값이 나온다.

def fetcher(url):
    get_df = pd.DataFrame(columns=['순서'])
    i = f"{os.getpid()} process | {threading.get_ident()} url : {url}"
    y = len(get_df)
    get_df.loc[y,"순서"]= y
    return get_df

이는 완벽한 해결책은 아니다.

이로인해서 처리해야 할 일이 더 늘 수도 있는데, 처리 속도보다 데이터의 신뢰도가 중요하다면(당연한 얘기지만) 추천한다.

반응형
저작자표시 비영리 변경금지 (새창열림)

'개발 > python' 카테고리의 다른 글

fastAPI python 값을 js의 변수에 저장하기  (0) 2023.03.21
파이썬 출력+로깅 같이하기 (파워쉘 Tee-Object)  (0) 2023.02.09
pyqt로 file open, file save 할 때 인코딩 주의점  (0) 2022.12.09
[python] PyQt5-tools 설치 에러 -> pyside2로 해결  (0) 2022.10.09
[파이썬] 요일 정렬하기 (무식하게)  (0) 2022.10.08
    '개발/python' 카테고리의 다른 글
    • fastAPI python 값을 js의 변수에 저장하기
    • 파이썬 출력+로깅 같이하기 (파워쉘 Tee-Object)
    • pyqt로 file open, file save 할 때 인코딩 주의점
    • [python] PyQt5-tools 설치 에러 -> pyside2로 해결
    북치기_개발모드
    북치기_개발모드
    북치기박치기 개발모드입니다.

    티스토리툴바