OkBublewrap

N배 빠른 크롤링(2) 본문

Python/학습용

N배 빠른 크롤링(2)

옥뽁뽁 2025. 2. 23. 02:28

스레드란?

스레드란 프로세스 내부에 있는 CPU 수행 단위

프로세스는 운영체제로부터 자원을 할당해 실행되는 작업의 단위

ex) 크롬 브라우저 두개 실행하면 두개의 프로세스 실행

 

threading

  • threading은 스레드를 이용하여 한 프로세스에서 2가지 이사으이 일을 동시에 실행할 수 있게 하는 표준 모듈
  • 파이썬은 기본적으로 싱글 스레드에서 순차적으로 동작함
  • 병렬 처리를 위해서는 별도 작업이 필요함
  • 활용 분야
    • 대용량 데이터의 처리시간을 줄이기 위해 데이터를 분할하여 병렬로 처리
    • 애플리케이션에서 다중 네트워크 통신을 할 때
    • 여러 클라이언트의 요청을 동시에 처리하는 서버를 개발할 때
from threading import Thread
import time

# 0부터 10,000,000 까지의 합을 구하는 프로그램
def work(id, start, end, result):
    total = 0
    for i in range(start, end):
        total += i
    result.append(total)
    return
    
if __name__ == "__main__":
    start = time.time()
    
    START, END = 0, 10000000
    result = list()
    th1 = Thread(target=work, args=(1, START, END, result)) # 싱글 스레드
    
    th1.start()
    th1.join()
    
    th1_elapsed = round(time.time() - start, 2)
    print(th1_elapsed, ' 초 경과')
    print(f"합계 결과 : {sum(result)}")

# 2.79 초 경과
# 합계 결과 : 49999995000000
    
if __name__ == "__main__":
    start = time.time()
    
    START, END = 0, 10000000
    result = list()
    th2 = Thread(target=work, args=(2, START, END, result))   # 멀티 스레드
    
    th2.start()
    th2.join()
    
    th2_elapsed = round(time.time() - start, 2)
    speed_up = round(th1_elapsed/th2_elapsed, 1)
    print(th2_elapsed, ' 초 경과')
    print(speed_up, ' 배 속도 향상')
    print(f"합계 결과: {sum(result)}")
    
# 1.52  초 경과
# 1.8  배 속도 향상
# 합계 결과: 49999995000000

 

multiprocessing

파이썬에서 병렬처리를 구현하는 방식은 멀티 쓰레드를 사용하거나 멀티 프로세스를 사용하는 두가지 방법이 있다.

 

스레드 특징

  • 스레드는 가볍지만 cpu 계산 처리를 하는 작업에는 한번에 하나의 쓰레드에서만 작동함
  • cpu 작업이 적고 네트워크 통신 또는 파일 읽고 쓰기와 같은 작업 (I/O)이 많은 병렬 처리 프로그램에서 효과적

 

멀티프로세싱 특징

  • 멀티 프로세서와 별개의 메모리를 사용하여 완전히 독립하여 병렬 프로그래밍이 가능
  • 여러 개의 CPU가 있는 멀티코어 환경에서 CPU 수 만큼 작업을 나누어 실행 가능
  • 프로세스는 각자가 고유한 메모리 영역을 가지기 때문에 더 많은 메모리를 필요
  • 각각 프로세스에서 병렬로 cpu 작업을 할 수 있고 이를 이용해 여러 장비에서 동작하는 분산 처리 프로그래밍도 구현이 가능

 

import multiprocessing
import time

# 0부터 50,000,000 까지의 합을 구하는 프로그램
def work(id, start, end, result):
    total = 0
    for i in range(start, end):
        total += i
    result.append(total)
    return

if __name__ == "__main__":
    start = time.time()
    
    START, END = 0, 50000000
    result = list()
    
    # 싱글 프로세스 2번 실행
    for i in range(2):
        work(1, START, END, result)
        print(f'{i+1} 번째 실행')
        
    
    time_elapsed1 = round(time.time() - start, 2)
    print(time_elapsed1, ' 초 경과')
    print(f"합계 결과: {sum(result)}")

# 1 번째 실행
# 2 번째 실행
# 10.65  초 경과
# 합계 결과: 2499999950000000 
   
if __name__ == "__main__":
    start = time.time()
    
    START, END = 0, 50000000
    result = list()
    
    procs = []
    for i in range(2):
        # 프로세스 수만큼 코어 할당
        p = multiprocessing.Process(target=work, args=(i, START, END, result))
        p.start()
        procs.append(p)
        print(f'{i+1} 번째 실행')

    for p in procs:
        p.join()  # 프로세스가 모두 종료될 때까지 대기
        
        
    time_elapsed2 = round(time.time() - start, 2)
    speed_up = round(time_elapsed1/time_elapsed2, 1)
    print(time_elapsed2, ' 초 경과')
    print(speed_up, ' 배 속도 향상')
    print(f"합계 결과: {sum(result)}")
    
# 1 번째 실행
# 2 번째 실행
# 5.86  초 경과
# 1.8  배 속도 향상
# 합계 결과: 0

 

결론

1️⃣ 싱글 프로세스 방식에서는 두 번의 계산을 순차적으로 진행하고, 결과를 result에 추가한 후 합계 계산

2️⃣ 멀티프로세스 방식에서는 두 프로세스를 병렬로 실행, result를 안전하게 안되서 잘못된 결과가 나옴

 

 

멀티프로세싱에서 여러 프로세스가 동일한 리스트를 수정하면, 경쟁 상태가 발생
이 경우 각 프로세스가 수정한 결과가 덮어씌워져 올바른 결과를 얻을 수 없다.

 

if __name__ == "__main__":
    start = time.time()
    
    START, END = 0, 50000000
    result = list()

    # Manager를 사용하여 멀티프로세싱에서 안전하게 공유할 수 있는 리스트 생성
    manager = multiprocessing.Manager()
    result = manager.list()  # 각 프로세스가 안전하게 수정할 수 있는 리스트

    procs = []
    for i in range(2):
        # 프로세스 수만큼 코어 할당
        p = multiprocessing.Process(target=work, args=(i, START, END, result))
        p.start()
        procs.append(p)
        print(f'{i+1} 번째 실행')

    for p in procs:
        p.join()  # 프로세스가 모두 종료될 때까지 대기
        
    time_elapsed2 = round(time.time() - start, 2)
    speed_up = round(time_elapsed1/time_elapsed2, 1)
    print(time_elapsed2, ' 초 경과')
    print(speed_up, ' 배 속도 향상')
    print(f"합계 결과: {sum(result)}")
# 1 번째 실행
# 2 번째 실행
# 5.47  초 경과
# 1.7  배 속도 향상
# 합계 결과: 2499999950000000

 

시스템 명령어 실행

subprocess

  • subprocess는 파이썬 스크립트 상에서 외부 프로세스 시스템 명령어를 실행할 때 사용하는 표준 모듈
  • 새로운 프로세스를 실행하도록 도와주며, 프로세서의 입/출력 및 에러 결과에 대한 리턴 값을 사용자가 직접 제어할 수 있게 도와줌
import subprocess
subprocess.run(['python', 'test.py'])

 

출력된 결과를 txt 파일로 저장

f = open('output.txt', 'w')
out = subprocess.checkout(['python', 'test.py'], encoding='utf-8')
f.write(out)
f.close()

 

'Python > 학습용' 카테고리의 다른 글

데이터 베이스 만들기  (0) 2025.02.23
N배 빠른 크롤링(3)  (0) 2025.02.23
N배 빠른 크롤링(1)  (0) 2025.02.23
[ML] 비지도학습  (0) 2025.01.29
Python-SQL 연결(데이터 추출)  (0) 2025.01.25