python 다중 프로세스와 다중 스레드 사용 상세 정보

프로세스 및 스레드


프로세스는 시스템이 자원 분배를 하는 최소 단위이고 라인은 시스템이 스케줄링 집행을 하는 최소 단위이다.
하나의 프로그램은 최소한 하나의 프로세스를 포함하고, 하나의 프로세스는 최소한 하나의 라인을 포함한다.
모든 프로세스는 실행 과정에서 독립된 메모리 공간을 가지고 하나의 프로세스의 스레드 사이는 이 프로세스의 메모리 공간을 공유한다.
  • 컴퓨터의 핵심은 CPU로 모든 계산 임무를 맡고 있다.그것은 마치 공장과 같아서 시시각각 운행하고 있다
  • 공장의 전력이 유한하다고 가정하면 한 번에 한 작업장에서만 사용할 수 있다.즉, 한 작업장이 착공할 때 다른 작업장은 반드시 휴업해야 한다는 것이다.하나의 CPU는 한 번에 하나의 작업만 실행할 수 있다는 의미입니다.편집자 주: 멀티 코어의 CPU는 마치 여러 개의 발전소가 있는 것처럼 멀티 공장(멀티 프로세스)을 실현할 수 있다
  • 프로세스는 마치 공장의 작업장과 같다. 이것은 CPU가 처리할 수 있는 단일 임무를 대표한다.CPU는 항상 하나의 프로세스를 실행하고 다른 프로세스는 비실행 상태입니다
  • 한 작업장에 많은 노동자가 있을 수 있다.그들은 협동하여 하나의 임무를 완성하였다
  • 라인은 작업장의 노동자와 같다.하나의 프로세스는 여러 개의 라인을 포함할 수 있다..
  • 작업장의 공간은 노동자들이 공유하는 것이다. 예를 들어 많은 방은 모든 노동자들이 출입할 수 있다.이것은 프로세스의 메모리 공간이 공유됨을 상징합니다. 모든 루트는 이 공유 메모리를 사용할 수 있습니다
  • 그러나 방마다 크기가 다르기 때문에 어떤 방은 최대 한 사람만 수용할 수 있다. 예를 들어 화장실이다.안에 사람이 있을 때 다른 사람들은 들어갈 수 없다.이것은 하나의 스레드가 일부 공유 메모리를 사용할 때, 다른 스레드가 끝날 때까지 기다려야만 이 메모리를 사용할 수 있다는 것을 나타낸다
  • 다른 사람이 들어오는 것을 방지하는 간단한 방법은 입구에 자물쇠를 넣는 것이다.먼저 도착한 사람은 문을 잠그고 나중에 도착한 사람은 자물쇠가 잠긴 것을 보고 입구에서 줄을 서서 자물쇠가 열리면 들어간다.이것을'상호 배열 자물쇠'(Mutual exclusion, 줄임말 Mutex)라고 하는데, 여러 개의 스레드가 하나의 메모리 영역을 동시에 읽는 것을 방지한다..
  • n명을 동시에 수용할 수 있는 방도 있다. 예를 들어 주방이다.n보다 많으면 밖에서 기다릴 수밖에 없다는 얘기다.이것은 마치 일부 메모리 영역과 같아서 고정된 수량의 라인만 사용할 수 있다
  • 이때의 해결 방법은 입구에 n자루의 열쇠를 거는 것이다.들어간 사람은 열쇠 한 자루를 꺼내서 나올 때 다시 열쇠를 제자리에 걸어라.뒤에 도착한 사람은 열쇠가 비어 있는 것을 발견하고 입구에서 줄을 서서 기다려야 한다는 것을 알았다.이런 방법을'신호량'(Semaphore)이라고 하는데, 여러 라인이 서로 충돌하지 않도록 보장하는 데 쓰인다
  • mutex는semaphore의 특수한 상황(n=1시)이다.전자를 후자로 대체할 수 있다는 얘기다.그러나mutex는 비교적 간단하고 효율이 높기 때문에 반드시 자원 독점을 확보해야 하는 상황에서 이런 디자인을 채택한다.
  • Python 멀티 프로세스


    Python의 다중 프로세스는multiprocess 모듈에 의존한다.다중 프로세스를 사용하면 여러 개의 CPU를 이용하여 병렬 계산을 할 수 있다.
    인스턴스:
    
    from multiprocessing import Process
    import os
    import time
     
    def long_time_task(i):
        print(' : {} -  {}'.format(os.getpid(), i))
        time.sleep(2)
        print(" : {}".format(8 ** 20))
     
    if __name__=='__main__':
        print(' : {}'.format(os.getpid()))
        start = time.time()
        p1 = Process(target=long_time_task, args=(1,))
        p2 = Process(target=long_time_task, args=(2,))
        print(' 。')
        p1.start()
        p2.start()
        p1.join()
        p2.join()
        end = time.time()
        print(" {} ".format((end - start)))
    새로 만든 프로세스와 프로세스 사이의 전환은 자원을 소모해야 하기 때문에 프로세스 수를 제어해야 한다.
    동시에 실행 가능한 프로세스 수는 CPU 코어 수 제한을 받습니다.

    프로세스 풀


    프로세스 풀 풀을 사용하여 프로세스를 만들려면 다음과 같이 하십시오.
    프로세스 풀을 사용하면 수동으로 프로세스를 만드는 번거로움을 피할 수 있습니다. 기본 수량은 CPU 핵 수입니다.
    Pool 클래스는 사용자가 사용할 수 있도록 지정한 수량의 프로세스를 제공합니다. 새로운 요청이 Pool에 제출되었을 때 프로세스 탱크가 가득 차지 않으면 요청을 실행하기 위한 새로운 프로세스를 만듭니다.풀이 가득 차면 요청이 기다리고 빈 프로세스가 사용될 때까지 기다립니다.
    몇 가지 방법:
    1.apply_async
    역할은 프로세스 탱크에 실행해야 할 함수와 파라미터를 제출하는 것이다. 각 프로세스는 막히지 않는 비동기적인 방식으로 호출되고 모든 프로세스는 자신의 운행에만 관여하며 기본적인 방식이다.
    2.map
    결과가 돌아올 때까지 프로세스가 막힙니다.
    3.map_sunc
    비차단 프로세스;
    4.close
    프로세스 풀을 닫고 작업을 더 이상 받지 않습니다.
    5.terminate
    프로세스 끝내기;
    6.join
    하위 프로세스가 끝날 때까지 주 프로세스가 막힙니다.
    인스턴스:
    
    from multiprocessing import Pool, cpu_count
    import os
    import time
     
    def long_time_task(i):
        print(' : {} -  {}'.format(os.getpid(), i))
        time.sleep(2)
        print(" : {}".format(8 ** 20))
     
    if __name__=='__main__':
        print("CPU :{}".format(cpu_count()))
        print(' : {}'.format(os.getpid()))
        start = time.time()
        p = Pool(4)
        for i in range(5):
            p.apply_async(long_time_task, args=(i,))
        print(' 。')
        p.close()
        p.join()
        end = time.time()
        print(" {} ".format((end - start)))
    join 전에close나terminate를 사용하여 프로세스 탱크가 더 이상 작업을 받지 않도록 해야 합니다.

    다중 프로세스 간의 데이터 통신과 공유


    일반적으로 프로세스는 서로 독립적이며, 모든 프로세스는 독립된 메모리를 가지고 있다.공유 메모리 (nmap 모듈) 를 통해 프로세스 간에 대상을 공유할 수 있으며, 여러 프로세스가 같은 변수에 접근할 수 있도록 합니다. (주소가 같고 변수 이름이 다를 수 있습니다.)다중 프로세스 공유 자원은 프로세스 간의 경쟁을 초래하기 때문에 가능한 한 공유 상태를 사용하지 않도록 해야 한다.또 다른 방식은 대기열queue를 사용하여 서로 다른 프로세스 간의 통신이나 데이터 공유를 실현하는 것이다. 이 점은 다중 루틴 프로그래밍과 유사하다.
    다음 예에서는 이 코드에 2개의 독립 프로세스를 만들었습니다. 하나는 쓰기(pw), 하나는 읽기(pr)를 맡고 하나의 대기열queue를 공유합니다.
    
    from multiprocessing import Process, Queue
    import os, time, random
     
    #  :
    def write(q):
        print('Process to write: {}'.format(os.getpid()))
        for value in ['A', 'B', 'C']:
            print('Put %s to queue...' % value)
            q.put(value)
            time.sleep(random.random())
     
    #  :
    def read(q):
        print('Process to read:{}'.format(os.getpid()))
        while True:
            value = q.get(True)
            print('Get %s from queue.' % value)
     
    if __name__=='__main__':
        #  Queue, :
        q = Queue()
        pw = Process(target=write, args=(q,))
        pr = Process(target=read, args=(q,))
        #  pw, :
        pw.start()
        #  pr, :
        pr.start()
        #  pw :
        pw.join()
        # pr , , :
        pr.terminate()

    Python의 다중 스레드


    python 3의 다중 프로세스 프로그래밍은 주로threading 모듈에 의존한다.새 루틴을 만드는 것은 새 프로세스를 만드는 방법과 매우 유사합니다.threading.Thread 방법은 두 개의 매개 변수를 수신할 수 있습니다. 첫 번째는 target입니다. 일반적으로 함수 이름을 가리키고, 두 번째는args입니다. 함수에 전달해야 하는 매개 변수입니다.새로 만든 스레드에 대해start () 방법을 호출하면 시작할 수 있습니다.우리는current_도 사용할 수 있다thread().name 현재 스레드의 이름을 출력합니다. 
    
    import threading
    import time
     
    def long_time_task(i):
        print(' : {}  {}'.format(threading.current_thread().name, i))
        time.sleep(2)
        print(" : {}".format(8 ** 20))
     
    if __name__=='__main__':
        start = time.time()
        print(' :{}'.format(threading.current_thread().name))
        thread_list = []
        for i in range(1, 3):
            t = threading.Thread(target=long_time_task, args=(i, ))
            thread_list.append(t)
        for t in thread_list:
            t.start()
        for t in thread_list:
            t.join()
        end = time.time()
        print(" {} ".format((end - start)))

    다중 스레드 간의 데이터 공유


    하나의 프로세스에 포함된 서로 다른 스레드 간의 공유 메모리는 모든 변수가 모든 스레드에 의해 수정될 수 있다는 것을 의미한다. 따라서 스레드 간의 공유 데이터의 가장 큰 위험은 여러 스레드가 동시에 하나의 변수를 바꾸어 내용을 혼란스럽게 하는 데 있다.만약 서로 다른 스레드 사이에 공유된 변수가 있다면, 그 중 하나는 수정하기 전에 자물쇠lock을 채워서 한 번에 하나의 스레드만 수정할 수 있도록 하는 것이다.threading.lock () 방법은 공유 변수에 대한 잠금을 쉽게 실현할 수 있으며, 수정이 끝난 후release는 다른 라인에서 사용할 수 있습니다.
    
    import threading
     
    class Account:
        def __init__(self):
            self.balance = 0
     
        def add(self, lock):
            #  
            lock.acquire()
            for i in range(0, 100000):
                self.balance += 1
            #  
            lock.release()
     
        def delete(self, lock):
            #  
            lock.acquire()
            for i in range(0, 100000):
                self.balance -= 1
                #  
            lock.release()
     
    if __name__ == "__main__":
        account = Account()
        lock = threading.Lock()
        #  
       thread_add = threading.Thread(target=account.add, args=(lock,), name='Add')
        thread_delete = threading.Thread(target=account.delete, args=(lock,), name='Delete')
     
        #  
       thread_add.start()
        thread_delete.start()
     
        #  
       thread_add.join()
        thread_delete.join()
     
        print('The final balance is: {}'.format(account.balance))

    queue 대기열 통신 사용 - 고전적인 생산자와 소비자 모델

    
    from queue import Queue
    import random, threading, time
     
    #  
    class Producer(threading.Thread):
        def __init__(self, name, queue):
            threading.Thread.__init__(self, name=name)
            self.queue = queue
     
        def run(self):
            for i in range(1, 5):
                print("{} is producing {} to the queue!".format(self.getName(), i))
                self.queue.put(i)
                time.sleep(random.randrange(10) / 5)
            print("%s finished!" % self.getName())
     
    #  
    class Consumer(threading.Thread):
        def __init__(self, name, queue):
            threading.Thread.__init__(self, name=name)
            self.queue = queue
     
        def run(self):
            for i in range(1, 5):
                val = self.queue.get()
                print("{} is consuming {} in the queue.".format(self.getName(), val))
                time.sleep(random.randrange(10))
            print("%s finished!" % self.getName())
     
    def main():
        queue = Queue()
        producer = Producer('Producer', queue)
        consumer = Consumer('Consumer', queue)
     
        producer.start()
        consumer.start()
     
        producer.join()
        consumer.join()
        print('All threads finished!')
     
    if __name__ == '__main__':
        main()
  • CPU 집약형 코드(예를 들어 순환 계산) - 다중 프로세스 효율이 높다
  • IO 집약형 코드(예를 들어 파일 조작, 네트워크 파충류) - 다중 스레드 효율이 높다
  • IO 집약형 조작에 있어서 대부분의 소모 시간은 사실 대기 시간이다. 대기 시간 중 CPU는 일을 할 필요가 없다. 그러면 이 기간에 이중 CPU 자원을 제공하는 것도 이용할 수 없다. 반대로 CPU 집약형 코드에 대해 2개의 CPU는 한 CPU보다 일을 많이 할 것이다.그렇다면 왜 다중 스레드가 IO 집약형 코드에 유용합니까?이때python이 대기에 부딪히면 GIL을 방출하여 새로운 스레드를 사용할 수 있도록 하여 스레드 간의 전환을 실현하였다.
    이상은python 다중 프로세스와 다중 스레드 사용에 대한 상세한 내용입니다. 더 많은python 다중 프로세스와 다중 스레드에 대한 자료는 저희의 다른 관련 글을 주목해 주십시오!

    좋은 웹페이지 즐겨찾기