KR_Multiprocessing - somaz94/python-study GitHub Wiki

Python ๋ฉ€ํ‹ฐํ”„๋กœ์„ธ์‹ฑ ๊ฐœ๋… ์ •๋ฆฌ


1๏ธโƒฃ ๋ฉ€ํ‹ฐํ”„๋กœ์„ธ์‹ฑ ๊ธฐ์ดˆ

๋ฉ€ํ‹ฐํ”„๋กœ์„ธ์‹ฑ์€ ์—ฌ๋Ÿฌ ํ”„๋กœ์„ธ์Šค๋ฅผ ๋™์‹œ์— ์‹คํ–‰ํ•˜์—ฌ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๊ธฐ์ˆ ์ด๋‹ค.

from multiprocessing import Process, current_process
import os
import time
from typing import List, Callable, Any, Optional, Dict, Union, Tuple

def basic_worker() -> None:
    """๊ธฐ๋ณธ ์ž‘์—…์ž ํ•จ์ˆ˜
    
    ํ˜„์žฌ ํ”„๋กœ์„ธ์Šค ์ •๋ณด๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค.
    """
    process = current_process()
    print(f'ํ”„๋กœ์„ธ์Šค ์ด๋ฆ„: {process.name}')
    print(f'ํ”„๋กœ์„ธ์Šค ID: {os.getpid()}')
    print(f'๋ถ€๋ชจ ํ”„๋กœ์„ธ์Šค ID: {os.getppid()}')
    print(f'ํ”„๋กœ์„ธ์Šค ์ƒํƒœ: {"alive" if process.is_alive() else "dead"}')

def worker_with_args(number: int, text: str) -> None:
    """์ธ์ž๋ฅผ ๋ฐ›๋Š” ์ž‘์—…์ž ํ•จ์ˆ˜
    
    Args:
        number: ์ •์ˆ˜ ์ธ์ž
        text: ๋ฌธ์ž์—ด ์ธ์ž
    """
    print(f'ํ”„๋กœ์„ธ์Šค ID: {os.getpid()}')
    print(f'์ธ์ž ๊ฐ’: number={number}, text={text}')
    time.sleep(2)  # ์ž‘์—… ์‹œ๋ฎฌ๋ ˆ์ด์…˜
    print(f'ํ”„๋กœ์„ธ์Šค {os.getpid()} ์ž‘์—… ์™„๋ฃŒ')

class ProcessManager:
    """ํ”„๋กœ์„ธ์Šค ๊ด€๋ฆฌ ํด๋ž˜์Šค"""
    
    def __init__(self, max_processes: int = 4):
        """์ดˆ๊ธฐํ™”
        
        Args:
            max_processes: ์ตœ๋Œ€ ํ”„๋กœ์„ธ์Šค ์ˆ˜
        """
        self.max_processes = max_processes
        self.processes: List[Process] = []
        
    def create_process(self, target: Callable, args: tuple = (), 
                     kwargs: Dict = None, name: Optional[str] = None) -> Process:
        """์ƒˆ ํ”„๋กœ์„ธ์Šค ์ƒ์„ฑ
        
        Args:
            target: ์‹คํ–‰ํ•  ํ•จ์ˆ˜
            args: ์œ„์น˜ ์ธ์ž
            kwargs: ํ‚ค์›Œ๋“œ ์ธ์ž
            name: ํ”„๋กœ์„ธ์Šค ์ด๋ฆ„
            
        Returns:
            Process: ์ƒ์„ฑ๋œ ํ”„๋กœ์„ธ์Šค ๊ฐ์ฒด
        """
        if kwargs is None:
            kwargs = {}
            
        p = Process(target=target, args=args, kwargs=kwargs, name=name)
        self.processes.append(p)
        return p
    
    def start_all(self) -> None:
        """๋ชจ๋“  ํ”„๋กœ์„ธ์Šค ์‹œ์ž‘"""
        for p in self.processes:
            p.start()
            print(f'ํ”„๋กœ์„ธ์Šค ์‹œ์ž‘: {p.name} (PID: {p.pid})')
    
    def join_all(self, timeout: Optional[float] = None) -> None:
        """๋ชจ๋“  ํ”„๋กœ์„ธ์Šค ๋Œ€๊ธฐ
        
        Args:
            timeout: ์ตœ๋Œ€ ๋Œ€๊ธฐ ์‹œ๊ฐ„(์ดˆ)
        """
        for p in self.processes:
            p.join(timeout)
    
    def terminate_all(self) -> None:
        """๋ชจ๋“  ํ”„๋กœ์„ธ์Šค ๊ฐ•์ œ ์ข…๋ฃŒ"""
        for p in self.processes:
            if p.is_alive():
                p.terminate()
                print(f'ํ”„๋กœ์„ธ์Šค ์ข…๋ฃŒ: {p.name} (PID: {p.pid})')
    
    def get_active_count(self) -> int:
        """ํ™œ์„ฑ ํ”„๋กœ์„ธ์Šค ์ˆ˜ ๋ฐ˜ํ™˜
        
        Returns:
            int: ํ™œ์„ฑ ํ”„๋กœ์„ธ์Šค ์ˆ˜
        """
        return sum(1 for p in self.processes if p.is_alive())

# ๊ธฐ๋ณธ ์‚ฌ์šฉ ์˜ˆ์‹œ
if __name__ == '__main__':
    # ๊ธฐ๋ณธ ํ”„๋กœ์„ธ์Šค ์ƒ์„ฑ ๋ฐ ์‹คํ–‰
    processes = []
    for i in range(3):
        p = Process(target=basic_worker)
        p.start()
        processes.append(p)
    
    for p in processes:
        p.join()
    
    print("\nํ”„๋กœ์„ธ์Šค ๋งค๋‹ˆ์ € ์‚ฌ์šฉ ์˜ˆ์‹œ:")
    # ํ”„๋กœ์„ธ์Šค ๋งค๋‹ˆ์ € ์‚ฌ์šฉ
    manager = ProcessManager()
    
    # ์—ฌ๋Ÿฌ ํ”„๋กœ์„ธ์Šค ์ƒ์„ฑ
    for i in range(3):
        manager.create_process(
            target=worker_with_args, 
            args=(i, f"์ž‘์—… {i}"),
            name=f"Worker-{i}"
        )
    
    # ๋ชจ๋“  ํ”„๋กœ์„ธ์Šค ์‹œ์ž‘ ๋ฐ ๋Œ€๊ธฐ
    manager.start_all()
    
    print(f"ํ™œ์„ฑ ํ”„๋กœ์„ธ์Šค ์ˆ˜: {manager.get_active_count()}")
    
    manager.join_all()
    print("๋ชจ๋“  ํ”„๋กœ์„ธ์Šค ์™„๋ฃŒ")

โœ… ํŠน์ง•:

  • ํ”„๋กœ์„ธ์Šค ์ƒ์„ฑ
  • ๋ณ‘๋ ฌ ์‹คํ–‰
  • ํ”„๋กœ์„ธ์Šค ๊ด€๋ฆฌ
  • ์ž์› ๊ฒฉ๋ฆฌ
  • ์•ˆ์ •์„ฑ ํ–ฅ์ƒ
  • ๋‹ค์ค‘ ์ฝ”์–ด ํ™œ์šฉ

๋ฉ€ํ‹ฐํ”„๋กœ์„ธ์‹ฑ vs ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ

ํŒŒ์ด์ฌ์—์„œ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ ๋‘ ๊ฐ€์ง€ ์ฃผ์š” ์ ‘๊ทผ ๋ฐฉ์‹์˜ ๋น„๊ต์ด๋‹ค.

ํŠน์„ฑ ๋ฉ€ํ‹ฐํ”„๋กœ์„ธ์‹ฑ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ
๋ฉ”๋ชจ๋ฆฌ ๊ณต์œ  ๋ณ„๋„ ๋ฉ”๋ชจ๋ฆฌ ๊ณต๊ฐ„ ๋ฉ”๋ชจ๋ฆฌ ๊ณต๊ฐ„ ๊ณต์œ 
GIL ์˜ํ–ฅ ์˜ํ–ฅ ์—†์Œ GIL์— ์˜ํ•ด ์ œํ•œ๋จ
์žฅ์  ์™„์ „ํ•œ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ, CPU ๋ฐ”์šด๋“œ ์ž‘์—…์— ์ ํ•ฉ ์ ์€ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ, I/O ๋ฐ”์šด๋“œ ์ž‘์—…์— ์ ํ•ฉ
๋‹จ์  ๋†’์€ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ, ํ”„๋กœ์„ธ์Šค ๊ฐ„ ํ†ต์‹  ๋น„์šฉ Python์˜ GIL๋กœ ์ธํ•œ ์„ฑ๋Šฅ ์ œํ•œ
์ƒ์„ฑ ๋น„์šฉ ๋†’์Œ ๋‚ฎ์Œ
์ ํ•ฉํ•œ ์ž‘์—… CPU ์ง‘์•ฝ์  ๊ณ„์‚ฐ, ๋ณ„๋„ ๋ฉ”๋ชจ๋ฆฌ ํ•„์š” ์ž‘์—… I/O ์ž‘์—…, ๋„คํŠธ์›Œํฌ ํ†ต์‹ , ๋А๋ฆฐ ์™ธ๋ถ€ ๋ฆฌ์†Œ์Šค ์ ‘๊ทผ


2๏ธโƒฃ ํ”„๋กœ์„ธ์Šค ๊ฐ„ ํ†ต์‹ 

์—ฌ๋Ÿฌ ํ”„๋กœ์„ธ์Šค ๊ฐ„์— ๋ฐ์ดํ„ฐ๋ฅผ ๊ตํ™˜ํ•˜๋Š” ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•์ด๋‹ค.

from multiprocessing import Process, Queue, Pipe, Event
import time
from typing import List, Dict, Any, Tuple, Optional

class QueueCommunication:
    """Queue๋ฅผ ์‚ฌ์šฉํ•œ ํ”„๋กœ์„ธ์Šค ๊ฐ„ ํ†ต์‹ """
    
    def sender(self, queue: Queue, data: List[Any]) -> None:
        """๋ฐ์ดํ„ฐ๋ฅผ ํ์— ์ „์†กํ•˜๋Š” ํ”„๋กœ์„ธ์Šค
        
        Args:
            queue: ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๋‚ผ ํ
            data: ๋ณด๋‚ผ ๋ฐ์ดํ„ฐ ๋ฆฌ์ŠคํŠธ
        """
        for item in data:
            queue.put(item)
            print(f'[Sender] ์ „์†ก: {item}')
            time.sleep(0.5)  # ์ „์†ก ๊ฐ„๊ฒฉ
        
        # ์ข…๋ฃŒ ์‹ ํ˜ธ ์ „์†ก
        queue.put(None)
        print('[Sender] ์ „์†ก ์™„๋ฃŒ')

    def receiver(self, queue: Queue) -> None:
        """ํ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์‹ ํ•˜๋Š” ํ”„๋กœ์„ธ์Šค
        
        Args:
            queue: ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์„ ํ
        """
        while True:
            item = queue.get()
            if item is None:  # ์ข…๋ฃŒ ์‹ ํ˜ธ ํ™•์ธ
                break
            print(f'[Receiver] ์ˆ˜์‹ : {item}')
        
        print('[Receiver] ์ˆ˜์‹  ์™„๋ฃŒ')
    
    def demonstrate(self, data: List[Any]) -> None:
        """Queue ํ†ต์‹  ์‹œ์—ฐ
        
        Args:
            data: ์ „์†กํ•  ๋ฐ์ดํ„ฐ
        """
        queue = Queue()
        
        sender_process = Process(
            target=self.sender, 
            args=(queue, data)
        )
        receiver_process = Process(
            target=self.receiver, 
            args=(queue,)
        )
        
        sender_process.start()
        receiver_process.start()
        
        sender_process.join()
        receiver_process.join()

class PipeCommunication:
    """Pipe๋ฅผ ์‚ฌ์šฉํ•œ ํ”„๋กœ์„ธ์Šค ๊ฐ„ ํ†ต์‹ """
    
    def pipe_process_a(self, conn: Pipe, data: List[Any]) -> None:
        """์ฒซ ๋ฒˆ์งธ ํ”„๋กœ์„ธ์Šค
        
        Args:
            conn: ํŒŒ์ดํ”„ ์—ฐ๊ฒฐ ๊ฐ์ฒด
            data: ์ „์†กํ•  ๋ฐ์ดํ„ฐ
        """
        for item in data:
            conn.send(item)
            print(f'[Process A] ์ „์†ก: {item}')
            
            # ์‘๋‹ต ๋Œ€๊ธฐ
            response = conn.recv()
            print(f'[Process A] ์ˆ˜์‹  ํ™•์ธ: {response}')
            
            time.sleep(0.5)
        
        # ์ข…๋ฃŒ ์‹ ํ˜ธ
        conn.send('END')
        conn.close()
    
    def pipe_process_b(self, conn: Pipe) -> None:
        """๋‘ ๋ฒˆ์งธ ํ”„๋กœ์„ธ์Šค
        
        Args:
            conn: ํŒŒ์ดํ”„ ์—ฐ๊ฒฐ ๊ฐ์ฒด
        """
        while True:
            item = conn.recv()
            if item == 'END':
                print('[Process B] ์ข…๋ฃŒ ์‹ ํ˜ธ ์ˆ˜์‹ ')
                break
                
            print(f'[Process B] ์ˆ˜์‹ : {item}')
            
            # ์‘๋‹ต ์ „์†ก
            conn.send(f'ACK: {item}')
        
        conn.close()
    
    def demonstrate(self, data: List[Any]) -> None:
        """Pipe ํ†ต์‹  ์‹œ์—ฐ
        
        Args:
            data: ์ „์†กํ•  ๋ฐ์ดํ„ฐ
        """
        # ์–‘๋ฐฉํ–ฅ ํŒŒ์ดํ”„ ์ƒ์„ฑ
        parent_conn, child_conn = Pipe()
        
        process_a = Process(
            target=self.pipe_process_a, 
            args=(parent_conn, data)
        )
        process_b = Process(
            target=self.pipe_process_b, 
            args=(child_conn,)
        )
        
        process_a.start()
        process_b.start()
        
        process_a.join()
        process_b.join()

class EventCommunication:
    """Event๋ฅผ ์‚ฌ์šฉํ•œ ํ”„๋กœ์„ธ์Šค ๊ฐ„ ๋™๊ธฐํ™”"""
    
    def wait_for_event(self, event: Event) -> None:
        """์ด๋ฒคํŠธ ๋Œ€๊ธฐ ํ”„๋กœ์„ธ์Šค
        
        Args:
            event: ๋Œ€๊ธฐํ•  ์ด๋ฒคํŠธ ๊ฐ์ฒด
        """
        print('[๋Œ€๊ธฐ ํ”„๋กœ์„ธ์Šค] ์ด๋ฒคํŠธ ๋Œ€๊ธฐ ์ค‘...')
        event.wait()  # ์ด๋ฒคํŠธ๊ฐ€ ์„ค์ •๋  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐ
        print('[๋Œ€๊ธฐ ํ”„๋กœ์„ธ์Šค] ์ด๋ฒคํŠธ ๋ฐœ์ƒ! ์ž‘์—… ์‹œ์ž‘')
        # ์ž‘์—… ์‹œ๋ฎฌ๋ ˆ์ด์…˜
        time.sleep(2)
        print('[๋Œ€๊ธฐ ํ”„๋กœ์„ธ์Šค] ์ž‘์—… ์™„๋ฃŒ')
    
    def trigger_event(self, event: Event, delay: float) -> None:
        """์ผ์ • ์‹œ๊ฐ„ ํ›„ ์ด๋ฒคํŠธ ํŠธ๋ฆฌ๊ฑฐ ํ”„๋กœ์„ธ์Šค
        
        Args:
            event: ์„ค์ •ํ•  ์ด๋ฒคํŠธ ๊ฐ์ฒด
            delay: ์ง€์—ฐ ์‹œ๊ฐ„(์ดˆ)
        """
        print(f'[ํŠธ๋ฆฌ๊ฑฐ ํ”„๋กœ์„ธ์Šค] {delay}์ดˆ ํ›„ ์ด๋ฒคํŠธ ์„ค์ • ์˜ˆ์ •')
        time.sleep(delay)
        print('[ํŠธ๋ฆฌ๊ฑฐ ํ”„๋กœ์„ธ์Šค] ์ด๋ฒคํŠธ ์„ค์ •!')
        event.set()  # ์ด๋ฒคํŠธ ์„ค์ •
    
    def demonstrate(self, delay: float = 3.0) -> None:
        """Event ํ†ต์‹  ์‹œ์—ฐ
        
        Args:
            delay: ์ด๋ฒคํŠธ ํŠธ๋ฆฌ๊ฑฐ ์ง€์—ฐ ์‹œ๊ฐ„(์ดˆ)
        """
        event = Event()
        
        wait_process = Process(
            target=self.wait_for_event, 
            args=(event,)
        )
        trigger_process = Process(
            target=self.trigger_event, 
            args=(event, delay)
        )
        
        wait_process.start()
        trigger_process.start()
        
        wait_process.join()
        trigger_process.join()

# ์‚ฌ์šฉ ์˜ˆ์‹œ
if __name__ == '__main__':
    # Queue ํ†ต์‹  ์˜ˆ์‹œ
    print("\n===== Queue ํ†ต์‹  ์˜ˆ์‹œ =====")
    queue_comm = QueueCommunication()
    queue_comm.demonstrate(['๋ฉ”์‹œ์ง€1', '๋ฉ”์‹œ์ง€2', '๋ฉ”์‹œ์ง€3'])
    
    # Pipe ํ†ต์‹  ์˜ˆ์‹œ
    print("\n===== Pipe ํ†ต์‹  ์˜ˆ์‹œ =====")
    pipe_comm = PipeCommunication()
    pipe_comm.demonstrate(['๋ฐ์ดํ„ฐ1', '๋ฐ์ดํ„ฐ2', '๋ฐ์ดํ„ฐ3'])
    
    # Event ํ†ต์‹  ์˜ˆ์‹œ
    print("\n===== Event ํ†ต์‹  ์˜ˆ์‹œ =====")
    event_comm = EventCommunication()
    event_comm.demonstrate(2.0)

โœ… ํŠน์ง•:

  • ํ ์‚ฌ์šฉ
  • ํŒŒ์ดํ”„ ํ†ต์‹ 
  • ๋ฐ์ดํ„ฐ ๊ตํ™˜
  • ๋น„๋™๊ธฐ ํ†ต์‹ 
  • ๋™๊ธฐํ™” ๋ฉ”์ปค๋‹ˆ์ฆ˜
  • ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜ ์กฐ์ •

ํ†ต์‹  ๋ฐฉ์‹ ๋น„๊ต

ํ”„๋กœ์„ธ์Šค ๊ฐ„ ํ†ต์‹ (IPC)์˜ ๋‹ค์–‘ํ•œ ๋ฐฉ์‹๊ณผ ํŠน์ง•์ด๋‹ค.

ํ†ต์‹  ๋ฐฉ์‹ ํŠน์ง• ์šฉ๋„
Queue ๋‹ค์ค‘ ์ƒ์‚ฐ์ž/์†Œ๋น„์ž, ์Šค๋ ˆ๋“œ ์•ˆ์ „ ์ž‘์—… ๋ถ„๋ฐฐ, ๊ฒฐ๊ณผ ์ˆ˜์ง‘
Pipe ์–‘๋ฐฉํ–ฅ ํ†ต์‹ , ์ผ๋ฐ˜์ ์œผ๋กœ ๋‘ ํ”„๋กœ์„ธ์Šค ๊ฐ„ ์‚ฌ์šฉ ์–‘๋ฐฉํ–ฅ ๋ฉ”์‹œ์ง€ ๊ตํ™˜
Value/Array ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ ๊ธฐ๋ฐ˜, ์›์ž์  ์—ฐ์‚ฐ ์ง€์› ๋‹จ์ˆœ ๋ฐ์ดํ„ฐ ๊ณต์œ 
Manager ๋ณต์žกํ•œ ๊ณต์œ  ๊ฐ์ฒด, ๋„คํŠธ์›Œํฌ ํˆฌ๋ช…์„ฑ ๋ณต์žกํ•œ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ ๊ณต์œ 
Event ํ”„๋กœ์„ธ์Šค ๊ฐ„ ๋™๊ธฐํ™” ์ƒํƒœ ์‹ ํ˜ธ, ์ž‘์—… ์กฐ์ •
Lock/RLock ์ƒํ˜ธ ๋ฐฐ์ œ, ๋ฆฌ์†Œ์Šค ๋ณดํ˜ธ ๊ณต์œ  ์ž์› ๋ณดํ˜ธ
Semaphore ๋ฆฌ์†Œ์Šค ์นด์šดํŒ…, ์ ‘๊ทผ ์ œํ•œ ๋ฆฌ์†Œ์Šค ํ’€ ๊ด€๋ฆฌ
Condition ์กฐ๊ฑด ๊ธฐ๋ฐ˜ ๋™๊ธฐํ™” ์ƒ์‚ฐ์ž/์†Œ๋น„์ž ํŒจํ„ด


3๏ธโƒฃ ํ”„๋กœ์„ธ์Šค ํ’€

๋ฐ˜๋ณต์ ์ธ ์ž‘์—…์„ ํšจ์œจ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ํ”„๋กœ์„ธ์Šค ํ’€ ํ™œ์šฉ ๋ฐฉ๋ฒ•์ด๋‹ค.

from multiprocessing import Pool, cpu_count
import time
import os
from typing import List, Callable, Any, Dict, Union, Tuple
import functools

def cpu_bound_task(x: int) -> int:
    """CPU ์ง‘์•ฝ์  ๊ณ„์‚ฐ ์ž‘์—… (์˜ˆ: ์†Œ์ˆ˜ ํŒ๋ณ„)
    
    Args:
        x: ์ž‘์—…ํ•  ์ˆซ์ž
        
    Returns:
        int: ๊ณ„์‚ฐ ๊ฒฐ๊ณผ
    """
    if x <= 1:
        return 0
    
    # ์†Œ์ˆ˜ ํŒ๋ณ„ (CPU ์ง‘์•ฝ์  ์ž‘์—… ์‹œ๋ฎฌ๋ ˆ์ด์…˜)
    count = 0
    for i in range(2, int(x**0.5) + 1):
        if x % i == 0:
            count += 1
    
    # ์ž‘์—… ์‹œ๊ฐ„ ์‹œ๋ฎฌ๋ ˆ์ด์…˜
    time.sleep(0.1)
    
    return count

def io_bound_task(filename: str) -> Tuple[str, int]:
    """I/O ์ง‘์•ฝ์  ์ž‘์—… (์˜ˆ: ํŒŒ์ผ ์ฒ˜๋ฆฌ)
    
    Args:
        filename: ์ฒ˜๋ฆฌํ•  ํŒŒ์ผ ์ด๋ฆ„
        
    Returns:
        Tuple[str, int]: (ํŒŒ์ผ๋ช…, ์ฒ˜๋ฆฌ ๊ฒฐ๊ณผ)
    """
    print(f"ํ”„๋กœ์„ธ์Šค {os.getpid()}: ํŒŒ์ผ {filename} ์ฒ˜๋ฆฌ ์ค‘")
    
    # I/O ์ž‘์—… ์‹œ๋ฎฌ๋ ˆ์ด์…˜
    time.sleep(0.5)
    
    # ์‹ค์ œ๋กœ๋Š” ์—ฌ๊ธฐ์„œ ํŒŒ์ผ์„ ์ฝ๊ณ  ์ฒ˜๋ฆฌ
    result = len(filename)
    
    return (filename, result)

def task_with_callback(x: int) -> int:
    """์ฝœ๋ฐฑ์„ ์œ„ํ•œ ์ž‘์—…
    
    Args:
        x: ์ž…๋ ฅ ๊ฐ’
        
    Returns:
        int: ๊ณ„์‚ฐ ๊ฒฐ๊ณผ
    """
    print(f"์ž‘์—… {x} ์‹œ์ž‘ (PID: {os.getpid()})")
    time.sleep(1)  # ์ž‘์—… ์‹œ๋ฎฌ๋ ˆ์ด์…˜
    result = x * x
    print(f"์ž‘์—… {x} ์™„๋ฃŒ")
    return result

def result_callback(result: int) -> None:
    """๋น„๋™๊ธฐ ์ž‘์—…์˜ ๊ฒฐ๊ณผ ์ฒ˜๋ฆฌ ์ฝœ๋ฐฑ
    
    Args:
        result: ์ž‘์—… ๊ฒฐ๊ณผ
    """
    print(f"์ฝœ๋ฐฑ: ๊ฒฐ๊ณผ = {result}")

class ProcessPoolManager:
    """ํ”„๋กœ์„ธ์Šค ํ’€ ๊ด€๋ฆฌ ํด๋ž˜์Šค"""
    
    def __init__(self, processes: int = None):
        """์ดˆ๊ธฐํ™”
        
        Args:
            processes: ํ”„๋กœ์„ธ์Šค ์ˆ˜ (None์ด๋ฉด CPU ์ฝ”์–ด ์ˆ˜ ์‚ฌ์šฉ)
        """
        self.processes = processes or cpu_count()
        print(f"ํ”„๋กœ์„ธ์Šค ํ’€ ํฌ๊ธฐ: {self.processes} (๊ฐ€์šฉ CPU: {cpu_count()})")
    
    def map_demonstration(self, func: Callable, iterable: List) -> List:
        """map ๋ฉ”์„œ๋“œ ์‹œ์—ฐ
        
        Args:
            func: ์ ์šฉํ•  ํ•จ์ˆ˜
            iterable: ์ž…๋ ฅ ๋ฐ์ดํ„ฐ ๋ฆฌ์ŠคํŠธ
            
        Returns:
            List: ๊ฒฐ๊ณผ ๋ฆฌ์ŠคํŠธ
        """
        start_time = time.time()
        
        with Pool(processes=self.processes) as pool:
            results = pool.map(func, iterable)
            
        end_time = time.time()
        print(f"map ์‹คํ–‰ ์‹œ๊ฐ„: {end_time - start_time:.2f}์ดˆ")
        
        return results
    
    def imap_unordered_demonstration(self, func: Callable, iterable: List) -> List:
        """imap_unordered ๋ฉ”์„œ๋“œ ์‹œ์—ฐ (์ˆœ์„œ ๋ฌด๊ด€, ๊ฒฐ๊ณผ ์ฆ‰์‹œ ๋ฐ˜ํ™˜)
        
        Args:
            func: ์ ์šฉํ•  ํ•จ์ˆ˜
            iterable: ์ž…๋ ฅ ๋ฐ์ดํ„ฐ ๋ฆฌ์ŠคํŠธ
            
        Returns:
            List: ๊ฒฐ๊ณผ ๋ฆฌ์ŠคํŠธ
        """
        start_time = time.time()
        results = []
        
        with Pool(processes=self.processes) as pool:
            for result in pool.imap_unordered(func, iterable):
                print(f"๊ฒฐ๊ณผ ๋„์ฐฉ: {result}")
                results.append(result)
        
        end_time = time.time()
        print(f"imap_unordered ์‹คํ–‰ ์‹œ๊ฐ„: {end_time - start_time:.2f}์ดˆ")
        
        return results
    
    def async_demonstration(self, func: Callable, args_list: List) -> List:
        """apply_async ๋ฉ”์„œ๋“œ ์‹œ์—ฐ (๋น„๋™๊ธฐ ์‹คํ–‰)
        
        Args:
            func: ์ ์šฉํ•  ํ•จ์ˆ˜
            args_list: ์ธ์ž ๋ฆฌ์ŠคํŠธ
            
        Returns:
            List: ๊ฒฐ๊ณผ ๋ฆฌ์ŠคํŠธ
        """
        results = []
        
        with Pool(processes=self.processes) as pool:
            async_results = [
                pool.apply_async(
                    func, 
                    args=(arg,),
                    callback=result_callback
                ) for arg in args_list
            ]
            
            # ๋ชจ๋“  ๊ฒฐ๊ณผ ์ˆ˜์ง‘
            for async_result in async_results:
                result = async_result.get()  # ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐ
                results.append(result)
        
        return results
    
    def chunksize_comparison(self, func: Callable, iterable: List) -> None:
        """์ฒญํฌ ํฌ๊ธฐ์— ๋”ฐ๋ฅธ ์„ฑ๋Šฅ ๋น„๊ต
        
        Args:
            func: ์ ์šฉํ•  ํ•จ์ˆ˜
            iterable: ์ž…๋ ฅ ๋ฐ์ดํ„ฐ ๋ฆฌ์ŠคํŠธ
        """
        chunk_sizes = [1, 10, 25, 50]
        
        for chunk_size in chunk_sizes:
            start_time = time.time()
            
            with Pool(processes=self.processes) as pool:
                results = pool.map(func, iterable, chunksize=chunk_size)
            
            end_time = time.time()
            print(f"์ฒญํฌ ํฌ๊ธฐ {chunk_size}: {end_time - start_time:.2f}์ดˆ")

# ์‚ฌ์šฉ ์˜ˆ์‹œ
if __name__ == '__main__':
    pool_manager = ProcessPoolManager()
    
    # 1. map ๋ฉ”์„œ๋“œ ์˜ˆ์‹œ (CPU ๋ฐ”์šด๋“œ ์ž‘์—…)
    print("\n===== Pool.map ์˜ˆ์‹œ (CPU ๋ฐ”์šด๋“œ) =====")
    numbers = list(range(100, 150))
    results = pool_manager.map_demonstration(cpu_bound_task, numbers)
    print(f"์ฒ˜๋ฆฌ๋œ ๊ฒฐ๊ณผ ์ˆ˜: {len(results)}")
    
    # 2. imap_unordered ์˜ˆ์‹œ (I/O ๋ฐ”์šด๋“œ ์ž‘์—…)
    print("\n===== Pool.imap_unordered ์˜ˆ์‹œ (I/O ๋ฐ”์šด๋“œ) =====")
    filenames = [f"file_{i}.txt" for i in range(1, 11)]
    results = pool_manager.imap_unordered_demonstration(io_bound_task, filenames)
    
    # 3. apply_async ์˜ˆ์‹œ (๋น„๋™๊ธฐ + ์ฝœ๋ฐฑ)
    print("\n===== Pool.apply_async ์˜ˆ์‹œ (์ฝœ๋ฐฑ ํฌํ•จ) =====")
    args_list = [3, 6, 9, 12]
    results = pool_manager.async_demonstration(task_with_callback, args_list)
    print(f"์ตœ์ข… ๊ฒฐ๊ณผ: {results}")
    
    # 4. ์ฒญํฌ ํฌ๊ธฐ์— ๋”ฐ๋ฅธ ์„ฑ๋Šฅ ๋น„๊ต
    print("\n===== ์ฒญํฌ ํฌ๊ธฐ์— ๋”ฐ๋ฅธ ์„ฑ๋Šฅ ๋น„๊ต =====")
    large_numbers = list(range(100, 300))
    pool_manager.chunksize_comparison(cpu_bound_task, large_numbers)

โœ… ํŠน์ง•:

  • ์ž‘์—… ๋ถ„๋ฐฐ
  • ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ
  • ๊ฒฐ๊ณผ ์ˆ˜์ง‘
  • ๋น„๋™๊ธฐ ์‹คํ–‰
  • ์ž๋™ ํ”„๋กœ์„ธ์Šค ๊ด€๋ฆฌ
  • ํšจ์œจ์ ์ธ ์ž์› ํ™œ์šฉ


4๏ธโƒฃ ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ

ํ”„๋กœ์„ธ์Šค ๊ฐ„์— ๋ฐ์ดํ„ฐ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ๊ณต์œ ํ•˜๊ธฐ ์œ„ํ•œ ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ ๋ฉ”์ปค๋‹ˆ์ฆ˜์ด๋‹ค.

from multiprocessing import Process, Value, Array, RawValue, RawArray
import time
import ctypes
from typing import List, Callable, Any

class SharedMemoryExample:
    """๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ ์˜ˆ์ œ"""
    
    def process_value(self, value: Value, operation: str) -> None:
        """Value ๊ฐ์ฒด ์กฐ์ž‘ ํ”„๋กœ์„ธ์Šค
        
        Args:
            value: ๊ณต์œ  Value ๊ฐ์ฒด
            operation: ์ˆ˜ํ–‰ํ•  ์—ฐ์‚ฐ (์ฆ๊ฐ€ ๋˜๋Š” ๊ฐ์†Œ)
        """
        for i in range(5):
            with value.get_lock():  # ๋ฝ ํš๋“
                if operation == 'increase':
                    value.value += 1
                elif operation == 'decrease':
                    value.value -= 1
                    
                print(f"[{operation}] ์ƒˆ ๊ฐ’: {value.value} (PID: {os.getpid()})")
            time.sleep(0.1)
    
    def process_array(self, shared_array: Array, start_idx: int) -> None:
        """Array ๊ฐ์ฒด ์กฐ์ž‘ ํ”„๋กœ์„ธ์Šค
        
        Args:
            shared_array: ๊ณต์œ  Array ๊ฐ์ฒด
            start_idx: ์‹œ์ž‘ ์ธ๋ฑ์Šค
        """
        for i in range(3):
            idx = start_idx + i
            if 0 <= idx < len(shared_array):
                with shared_array.get_lock():  # ๋ฝ ํš๋“
                    shared_array[idx] = shared_array[idx] * 2
                    print(f"[PID: {os.getpid()}] ์ธ๋ฑ์Šค {idx} ๊ฐ’ ๋ณ€๊ฒฝ: {shared_array[idx]}")
            time.sleep(0.2)
    
    def demonstrate_value(self) -> None:
        """Value ๊ฐ์ฒด ๋ฐ๋ชจ"""
        # ์ดˆ๊ธฐ๊ฐ’ 0์ธ ์ •์ˆ˜ํ˜• ๊ณต์œ  ๋ณ€์ˆ˜ ์ƒ์„ฑ
        shared_value = Value('i', 0)
        
        # ๋‘ ํ”„๋กœ์„ธ์Šค ์ƒ์„ฑ
        p1 = Process(target=self.process_value, args=(shared_value, 'increase'))
        p2 = Process(target=self.process_value, args=(shared_value, 'decrease'))
        
        p1.start()
        p2.start()
        
        p1.join()
        p2.join()
        
        print(f"์ตœ์ข… ๊ฐ’: {shared_value.value}")
    
    def demonstrate_array(self) -> None:
        """Array ๊ฐ์ฒด ๋ฐ๋ชจ"""
        # ์ดˆ๊ธฐ๊ฐ’ [1,2,3,4,5]์ธ ์ •์ˆ˜ํ˜• ๊ณต์œ  ๋ฐฐ์—ด ์ƒ์„ฑ
        shared_array = Array('i', [1, 2, 3, 4, 5])
        
        processes = []
        for i in range(3):
            p = Process(target=self.process_array, args=(shared_array, i))
            processes.append(p)
            p.start()
        
        for p in processes:
            p.join()
        
        print(f"์ตœ์ข… ๋ฐฐ์—ด: {list(shared_array)}")
    
    def demonstrate_raw_types(self) -> None:
        """Raw ํƒ€์ž… ๋ฐ๋ชจ (๋ฝ ์—†์Œ)"""
        # ๋ฝ ์—†๋Š” ๊ณต์œ  ๋ณ€์ˆ˜
        raw_value = RawValue(ctypes.c_double, 3.14)
        
        # ๋ฝ ์—†๋Š” ๊ณต์œ  ๋ฐฐ์—ด
        raw_array = RawArray(ctypes.c_int, 5)
        for i in range(5):
            raw_array[i] = i * 2
        
        def modify_raw(rv, ra):
            rv.value += 1.0
            for i in range(len(ra)):
                ra[i] += 10
            print(f"Raw ๊ฐ’ ์ˆ˜์ •: {rv.value}, ๋ฐฐ์—ด: {[ra[i] for i in range(len(ra))]}")
        
        processes = []
        for _ in range(2):
            p = Process(target=modify_raw, args=(raw_value, raw_array))
            processes.append(p)
            p.start()
        
        for p in processes:
            p.join()
        
        print(f"์ตœ์ข… Raw ๊ฐ’: {raw_value.value}")
        print(f"์ตœ์ข… Raw ๋ฐฐ์—ด: {[raw_array[i] for i in range(5)]}")

class SharedCounterExample:
    """๊ณต์œ  ์นด์šดํ„ฐ ์˜ˆ์ œ"""
    
    def __init__(self):
        """๊ณต์œ  ์นด์šดํ„ฐ ์ดˆ๊ธฐํ™”"""
        self.counter = Value('i', 0)
    
    def increment(self, n: int) -> None:
        """์นด์šดํ„ฐ ์ฆ๊ฐ€
        
        Args:
            n: ์ฆ๊ฐ€์‹œํ‚ฌ ํšŸ์ˆ˜
        """
        for _ in range(n):
            with self.counter.get_lock():
                self.counter.value += 1
    
    def run_workers(self, num_processes: int, increments_per_process: int) -> None:
        """์—ฌ๋Ÿฌ ํ”„๋กœ์„ธ์Šค๋กœ ์นด์šดํ„ฐ ์ฆ๊ฐ€
        
        Args:
            num_processes: ์ƒ์„ฑํ•  ํ”„๋กœ์„ธ์Šค ์ˆ˜
            increments_per_process: ํ”„๋กœ์„ธ์Šค๋‹น ์ฆ๊ฐ€ ํšŸ์ˆ˜
        """
        processes = []
        for _ in range(num_processes):
            p = Process(target=self.increment, args=(increments_per_process,))
            processes.append(p)
            p.start()
        
        for p in processes:
            p.join()
        
        expected = num_processes * increments_per_process
        print(f"์˜ˆ์ƒ ์นด์šดํ„ฐ ๊ฐ’: {expected}")
        print(f"์‹ค์ œ ์นด์šดํ„ฐ ๊ฐ’: {self.counter.value}")
        print(f"์ •ํ™•์„ฑ: {'์ •ํ™•ํ•จ' if expected == self.counter.value else '๊ฒฝ์Ÿ ์ƒํƒœ ๋ฐœ์ƒ'}")

# ์‚ฌ์šฉ ์˜ˆ์‹œ
if __name__ == '__main__':
    import os
    
    # ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ ์˜ˆ์ œ
    print("\n===== ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ ์˜ˆ์ œ =====")
    shared_mem = SharedMemoryExample()
    
    print("\n[Value ๊ฐ์ฒด ์‚ฌ์šฉ]")
    shared_mem.demonstrate_value()
    
    print("\n[Array ๊ฐ์ฒด ์‚ฌ์šฉ]")
    shared_mem.demonstrate_array()
    
    print("\n[Raw ํƒ€์ž… ์‚ฌ์šฉ]")
    shared_mem.demonstrate_raw_types()
    
    # ๊ณต์œ  ์นด์šดํ„ฐ ์˜ˆ์ œ
    print("\n===== ๊ณต์œ  ์นด์šดํ„ฐ ๊ฒฝ์Ÿ ์ƒํƒœ ์˜ˆ๋ฐฉ =====")
    counter = SharedCounterExample()
    counter.run_workers(num_processes=4, increments_per_process=10000)

โœ… ํŠน์ง•:

  • ๊ณต์œ  ๋ณ€์ˆ˜
  • ๊ณต์œ  ๋ฐฐ์—ด
  • ๋ฉ”๋ชจ๋ฆฌ ํšจ์œจ์„ฑ
  • ํ”„๋กœ์„ธ์Šค ๊ฐ„ ๋™๊ธฐํ™”
  • ์›์ž์  ์—ฐ์‚ฐ
  • ํƒ€์ž… ์•ˆ์ „์„ฑ

๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ ํƒ€์ž… ๋น„๊ต

๋ฉ€ํ‹ฐํ”„๋กœ์„ธ์‹ฑ์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋‹ค์–‘ํ•œ ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ ํƒ€์ž…์ด๋‹ค.

ํƒ€์ž… ๋ฝ ์ œ๊ณต ์‚ฌ์šฉ ์‚ฌ๋ก€ ํŠน์ง•
Value โœ“ ๋‹จ์ผ ๊ฐ’ ๊ณต์œ  ๋‚ด์žฅ ๋ฝ, ํƒ€์ž… ์•ˆ์ „์„ฑ
Array โœ“ ๋ฐฐ์—ด ๊ณต์œ  ๋‚ด์žฅ ๋ฝ, ๊ณ ์ • ํฌ๊ธฐ, ๋™์ข… ํƒ€์ž…
RawValue โœ— ์„ฑ๋Šฅ ์ค‘์‹ฌ ๋‹จ์ผ ๊ฐ’ ๋ฝ ์—†์Œ, ์ง์ ‘ ๋™๊ธฐํ™” ํ•„์š”
RawArray โœ— ์„ฑ๋Šฅ ์ค‘์‹ฌ ๋ฐฐ์—ด ๋ฝ ์—†์Œ, ์ง์ ‘ ๋™๊ธฐํ™” ํ•„์š”
Manager ๊ฐ์ฒด โœ“ ๋ณต์žกํ•œ ์ž๋ฃŒ๊ตฌ์กฐ ๋„คํŠธ์›Œํฌ ํˆฌ๋ช…์„ฑ, ์œ ์—ฐ์„ฑ
shared_memory โœ— ๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ Python 3.8+, ์ง์ ‘ ๊ด€๋ฆฌ


5๏ธโƒฃ ๋งค๋‹ˆ์ € ๊ฐ์ฒด

๋‹ค์–‘ํ•œ ๋ณต์žกํ•œ ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ํ”„๋กœ์„ธ์Šค ๊ฐ„์— ์•ˆ์ „ํ•˜๊ฒŒ ๊ณต์œ ํ•˜๊ธฐ ์œ„ํ•œ ๋งค๋‹ˆ์ € ๊ฐ์ฒด์ด๋‹ค.

from multiprocessing import Process, Manager, Lock
import time
import random
from typing import Dict, List, Any, Set, DefaultDict
import collections

class ManagerExample:
    """Manager ๊ฐ์ฒด ์‚ฌ์šฉ ์˜ˆ์ œ"""
    
    def worker_dict(self, shared_dict: Dict, worker_id: int, keys: List[str]) -> None:
        """๊ณต์œ  ๋”•์…”๋„ˆ๋ฆฌ ์ž‘์—… ํ”„๋กœ์„ธ์Šค
        
        Args:
            shared_dict: ๊ณต์œ  ๋”•์…”๋„ˆ๋ฆฌ
            worker_id: ์ž‘์—…์ž ID
            keys: ์ž‘์—…ํ•  ํ‚ค ๋ชฉ๋ก
        """
        for key in keys:
            # ํ˜„์žฌ ๊ฐ’ ์ฝ๊ธฐ
            current = shared_dict.get(key, 0)
            # ๊ฐ’ ์—…๋ฐ์ดํŠธ
            shared_dict[key] = current + worker_id
            print(f"Worker {worker_id}: ํ‚ค '{key}' ๊ฐ’์„ {current} โ†’ {shared_dict[key]}๋กœ ์—…๋ฐ์ดํŠธ")
            time.sleep(random.random() * 0.3)  # ์ž„์˜ ์ง€์—ฐ
    
    def worker_list(self, shared_list: List, items: List[int]) -> None:
        """๊ณต์œ  ๋ฆฌ์ŠคํŠธ ์ž‘์—… ํ”„๋กœ์„ธ์Šค
        
        Args:
            shared_list: ๊ณต์œ  ๋ฆฌ์ŠคํŠธ
            items: ์ถ”๊ฐ€ํ•  ํ•ญ๋ชฉ ๋ชฉ๋ก
        """
        for item in items:
            shared_list.append(item)
            print(f"ํ”„๋กœ์„ธ์Šค {os.getpid()}: ํ•ญ๋ชฉ {item} ์ถ”๊ฐ€๋จ, ํ˜„์žฌ ๋ฆฌ์ŠคํŠธ: {list(shared_list)}")
            time.sleep(random.random() * 0.2)  # ์ž„์˜ ์ง€์—ฐ
    
    def worker_namespace(self, ns: Any, worker_id: int) -> None:
        """๊ณต์œ  ๋„ค์ž„์ŠคํŽ˜์ด์Šค ์ž‘์—… ํ”„๋กœ์„ธ์Šค
        
        Args:
            ns: ๊ณต์œ  ๋„ค์ž„์ŠคํŽ˜์ด์Šค
            worker_id: ์ž‘์—…์ž ID
        """
        # ์†์„ฑ ์—…๋ฐ์ดํŠธ
        current = getattr(ns, f'count_{worker_id}', 0)
        setattr(ns, f'count_{worker_id}', current + 1)
        
        # ๊ณต์œ  total ๊ฐ’ ์—…๋ฐ์ดํŠธ
        ns.total += 1
        
        print(f"Worker {worker_id}: count_{worker_id}={getattr(ns, f'count_{worker_id}')}, total={ns.total}")
        time.sleep(0.1)
    
    def demonstrate_dict(self) -> None:
        """๊ณต์œ  ๋”•์…”๋„ˆ๋ฆฌ ๋ฐ๋ชจ"""
        with Manager() as manager:
            # ๊ณต์œ  ๋”•์…”๋„ˆ๋ฆฌ ์ƒ์„ฑ
            shared_dict = manager.dict()
            shared_dict['์ดˆ๊ธฐ๊ฐ’'] = 100
            
            processes = []
            all_keys = ['a', 'b', 'c', 'd', 'e']
            
            # ์ž‘์—…์ž๋ณ„๋กœ ํ‚ค ๋ถ„๋ฐฐ
            for i in range(3):
                keys = all_keys[i:i+3]  # ์ผ๋ถ€ ํ‚ค ๊ฒน์นจ
                p = Process(target=self.worker_dict, args=(shared_dict, i+1, keys))
                processes.append(p)
                p.start()
            
            for p in processes:
                p.join()
            
            # ์ตœ์ข… ๊ฒฐ๊ณผ ์ถœ๋ ฅ
            print(f"์ตœ์ข… ๋”•์…”๋„ˆ๋ฆฌ: {dict(shared_dict)}")
    
    def demonstrate_list(self) -> None:
        """๊ณต์œ  ๋ฆฌ์ŠคํŠธ ๋ฐ๋ชจ"""
        with Manager() as manager:
            # ๊ณต์œ  ๋ฆฌ์ŠคํŠธ ์ƒ์„ฑ
            shared_list = manager.list([0, 10, 20])
            
            processes = []
            # ์—ฌ๋Ÿฌ ํ”„๋กœ์„ธ์Šค๊ฐ€ ๋ฆฌ์ŠคํŠธ์— ํ•ญ๋ชฉ ์ถ”๊ฐ€
            for i in range(3):
                items = list(range(i*10+1, i*10+4))
                p = Process(target=self.worker_list, args=(shared_list, items))
                processes.append(p)
                p.start()
            
            for p in processes:
                p.join()
            
            # ์ตœ์ข… ๊ฒฐ๊ณผ ์ถœ๋ ฅ
            print(f"์ตœ์ข… ๋ฆฌ์ŠคํŠธ: {list(shared_list)}")
    
    def demonstrate_namespace(self) -> None:
        """๊ณต์œ  ๋„ค์ž„์ŠคํŽ˜์ด์Šค ๋ฐ๋ชจ"""
        with Manager() as manager:
            # ๊ณต์œ  ๋„ค์ž„์ŠคํŽ˜์ด์Šค ์ƒ์„ฑ
            ns = manager.Namespace()
            ns.total = 0
            
            processes = []
            for i in range(1, 5):
                setattr(ns, f'count_{i}', 0)  # ์ดˆ๊ธฐํ™”
                p = Process(target=self.worker_namespace, args=(ns, i))
                processes.append(p)
                p.start()
            
            for p in processes:
                p.join()
            
            # ๊ฒฐ๊ณผ ์ถœ๋ ฅ
            print("\n์ตœ์ข… ๋„ค์ž„์ŠคํŽ˜์ด์Šค ์ƒํƒœ:")
            print(f"total: {ns.total}")
            for i in range(1, 5):
                print(f"count_{i}: {getattr(ns, f'count_{i}')}")
    
    def demonstrate_complex_structures(self) -> None:
        """๋ณตํ•ฉ ์ž๋ฃŒ๊ตฌ์กฐ ๋ฐ๋ชจ"""
        with Manager() as manager:
            # ๋‹ค์–‘ํ•œ ๊ณต์œ  ์ปฌ๋ ‰์…˜ ์ƒ์„ฑ
            shared_list = manager.list()
            shared_dict = manager.dict()
            shared_set = manager.set()
            
            # ๋ฐ”๋กœ ํ•จ์ˆ˜ ์ •์˜
            def complex_worker(lock, list_obj, dict_obj, set_obj, worker_id):
                with lock:
                    # ๋ชจ๋“  ๊ณต์œ  ๊ฐ์ฒด ์—…๋ฐ์ดํŠธ
                    list_obj.append(worker_id)
                    dict_obj[f'worker_{worker_id}'] = worker_id * 10
                    set_obj.add(worker_id * 100)
                    
                    print(f"Worker {worker_id} ์—…๋ฐ์ดํŠธ:")
                    print(f"  ๋ฆฌ์ŠคํŠธ: {list(list_obj)}")
                    print(f"  ๋”•์…”๋„ˆ๋ฆฌ: {dict(dict_obj)}")
                    print(f"  ์ง‘ํ•ฉ: {set(set_obj)}")
            
            # ๊ณต์œ  ๋ฝ ์ƒ์„ฑ
            lock = manager.Lock()
            
            processes = []
            for i in range(1, 4):
                p = Process(target=complex_worker, args=(lock, shared_list, shared_dict, shared_set, i))
                processes.append(p)
                p.start()
            
            for p in processes:
                p.join()
            
            # ์ตœ์ข… ๊ฒฐ๊ณผ ์ถœ๋ ฅ
            print("\n์ตœ์ข… ๊ฒฐ๊ณผ:")
            print(f"๋ฆฌ์ŠคํŠธ: {list(shared_list)}")
            print(f"๋”•์…”๋„ˆ๋ฆฌ: {dict(shared_dict)}")
            print(f"์ง‘ํ•ฉ: {set(shared_set)}")

# ์‚ฌ์šฉ ์˜ˆ์‹œ
if __name__ == '__main__':
    import os
    
    manager_example = ManagerExample()
    
    # ๋”•์…”๋„ˆ๋ฆฌ ์˜ˆ์ œ
    print("\n===== ๊ณต์œ  ๋”•์…”๋„ˆ๋ฆฌ ์˜ˆ์ œ =====")
    manager_example.demonstrate_dict()
    
    # ๋ฆฌ์ŠคํŠธ ์˜ˆ์ œ
    print("\n===== ๊ณต์œ  ๋ฆฌ์ŠคํŠธ ์˜ˆ์ œ =====")
    manager_example.demonstrate_list()
    
    # ๋„ค์ž„์ŠคํŽ˜์ด์Šค ์˜ˆ์ œ
    print("\n===== ๊ณต์œ  ๋„ค์ž„์ŠคํŽ˜์ด์Šค ์˜ˆ์ œ =====")
    manager_example.demonstrate_namespace()
    
    # ๋ณตํ•ฉ ์ž๋ฃŒ๊ตฌ์กฐ ์˜ˆ์ œ
    print("\n===== ๋ณตํ•ฉ ์ž๋ฃŒ๊ตฌ์กฐ ์˜ˆ์ œ =====")
    manager_example.demonstrate_complex_structures()

โœ… ํŠน์ง•:

  • ๋ณต์žกํ•œ ์ž๋ฃŒ๊ตฌ์กฐ ๊ณต์œ 
  • ๋ถ„์‚ฐ ์‹œ์Šคํ…œ ์ง€์›
  • ๋„คํŠธ์›Œํฌ ํˆฌ๋ช…์„ฑ
  • ์œ ์—ฐํ•œ ๋™๊ธฐํ™”
  • ์ž๋™ ์ง๋ ฌํ™”
  • ํ”„๋ก์‹œ ๊ฐ์ฒด ์‚ฌ์šฉ

Manager vs ๋‹ค๋ฅธ ๊ณต์œ  ๋ฉ”์ปค๋‹ˆ์ฆ˜

Manager ๊ฐ์ฒด์™€ ๋‹ค๋ฅธ ๊ณต์œ  ๋ฐฉ๋ฒ•์˜ ๋น„๊ต์ด๋‹ค.

ํŠน์„ฑ Manager Value/Array Queue/Pipe
์ž๋ฃŒ๊ตฌ์กฐ ๋ณต์žก์„ฑ ๋ณต์žกํ•œ ์ค‘์ฒฉ ๊ตฌ์กฐ ๋‹จ์ˆœ ํƒ€์ž… ๋ฉ”์‹œ์ง€ ๊ธฐ๋ฐ˜
์„ฑ๋Šฅ ์ƒ๋Œ€์ ์œผ๋กœ ๋А๋ฆผ ๋น ๋ฆ„ ๋ณดํ†ต
์‚ฌ์šฉ ํŽธ์˜์„ฑ ๋†’์Œ ์ค‘๊ฐ„ ์ค‘๊ฐ„
๋„คํŠธ์›Œํฌ ์ง€์› ์ง€์› ๋ฏธ์ง€์› ๋ฏธ์ง€์›
์ œ๊ณต ๊ฐ์ฒด ๋”•์…”๋„ˆ๋ฆฌ, ๋ฆฌ์ŠคํŠธ, ๋„ค์ž„์ŠคํŽ˜์ด์Šค, ํ, ๋ฝ, ๋“ฑ ๋‹จ์ผ ๊ฐ’, ๋ฐฐ์—ด ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆผ
์šฉ๋„ ๋ณต์žกํ•œ ๊ณต์œ , ๋„คํŠธ์›Œํฌ ํˆฌ๋ช…์„ฑ ๋น ๋ฅธ ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ ๋ฐ์ดํ„ฐ ํŒŒ์ดํ”„๋ผ์ธ


์ฃผ์š” ํŒ

โœ… ๋ชจ๋ฒ” ์‚ฌ๋ก€:

  • ํ”„๋กœ์„ธ์Šค ์ƒ์„ฑ ๋น„์šฉ ์ดํ•ด
  • ์ ์ ˆํ•œ IPC ๋ฉ”์ปค๋‹ˆ์ฆ˜ ์„ ํƒ
  • Pool ํฌ๊ธฐ ์ตœ์ ํ™”
  • ์ž์› ๊ด€๋ฆฌ ์ฃผ์˜
  • ๊ฒฝ์Ÿ ์ƒํƒœ ๋ฐฉ์ง€
  • ๊ต์ฐฉ ์ƒํƒœ ์˜ˆ๋ฐฉ
  • ํ”„๋กœ์„ธ์Šค ์ข…๋ฃŒ ์ฒ˜๋ฆฌ
  • ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๊ตฌํ˜„
  • ๋กœ๊น… ์ „๋žต ์ˆ˜๋ฆฝ
  • ๋ถ€ํ•˜ ๋ถ„์‚ฐ ๊ณ ๋ ค


โš ๏ธ **GitHub.com Fallback** โš ๏ธ