Python 공유 메모리 문제 수정 및 공유 리소스 잠금

Salman Mehmood 2023년6월21일
  1. multiprocessing.Array()를 사용하여 Python에서 공유 메모리 사용
  2. multiprocessing.Lock()을 사용하여 Python에서 공유 리소스 잠금
Python 공유 메모리 문제 수정 및 공유 리소스 잠금

이 자습서에서는 다중 처리 공유 메모리의 다양한 측면을 설명하고 공유 메모리를 사용하여 문제를 해결하는 방법을 보여줍니다. 또한 잠금을 사용하여 Python에서 공유 리소스를 잠그는 방법도 배웁니다.

multiprocessing.Array()를 사용하여 Python에서 공유 메모리 사용

다중 처리의 가장 중요한 측면 중 하나는 여러 하위 프로세스가 있을 때 프로세스 간에 데이터를 공유하는 것입니다.

processing 모듈의 기능을 사용하여 생성하는 자식 프로세스의 필수 속성 중 하나는 독립적으로 실행되고 자체 메모리 공간이 있다는 것입니다.

이는 자식 프로세스에 약간의 메모리 공간이 있음을 의미합니다. 그리고 모든 변수는 부모의 메모리 공간이 아닌 자체 메모리 공간에서 생성을 시도하거나 변경됩니다.

예제를 통해 이 개념을 이해하고 multiprocessing 모듈을 가져와 코드를 살펴보겠습니다.

RESULT라는 빈 목록을 만들고 지정된 목록의 요소를 제곱하고 글로벌 RESULT 목록에 추가하는 Make_Sqaured_List()라는 함수를 정의했습니다.

Procc_1 개체는 Process() 클래스와 동일하며 대상을 대괄호 없이 Make_Sqaured_List 함수로 설정합니다.

그리고 args 매개변수에는 Make_Sqaured_List() 함수의 인수로 제공될 Items_list라는 별도의 목록을 전달합니다.

예제 코드:

import multiprocessing

RESULT = []


def Make_Sqaured_List(Num_List):
    global RESULT

    for n in Num_List:
        RESULT.append(n ** 2)
    print(f"Result: {RESULT}")


if __name__ == "__main__":
    Items_list = [5, 6, 7, 8]

    Procc_1 = multiprocessing.Process(target=Make_Sqaured_List, args=(Items_list,))
    Procc_1.start()
    Procc_1.join()

이 자식 프로세스를 실행하고 우리 자식 프로세스에 따라 전역 목록의 값이 되는 결과를 얻습니다.

Result: [25, 36, 49, 64]

그러나 여전히 비어 있는 RESULT 목록을 인쇄하려고 하면 RESULT 목록에 어떤 일이 발생합니까?

print(RESULT)

출력:

[]

메인 프로세스에 따르면 상위 프로세스는 여전히 비어 있는 반면 하위 프로세스에 따르면 RESULT 목록에는 일부 내용이 있습니다. 그것은 단순히 우리의 서로 다른 두 프로세스가 서로 다른 메모리 공간을 가지고 있음을 의미합니다.

처음에 빈 RESULT 목록이 있는 기본 프로그램인 프로세스 1이 있는 시나리오로 이를 이해할 수 있습니다.

그리고 우리가 자식 프로세스를 생성할 때 초기에 비어 있는 상태에서 Make_Sqaured_List() 함수가 실행되므로 RESULT 목록에 일부 항목이 포함됩니다. 그러나 이 메모리 공간의 상위 프로세스에서 RESULT에 액세스하고 있으므로 변경 사항이 표시되지 않습니다.

예제 코드:

import multiprocessing

RESULT = []


def Make_Sqaured_List(Num_List):
    global RESULT

    for n in Num_List:
        RESULT.append(n ** 2)
    print(f"Result: {RESULT}")


if __name__ == "__main__":
    Items_list = [5, 6, 7, 8]

    Procc_1 = multiprocessing.Process(target=Make_Sqaured_List, args=(Items_list,))
    Procc_1.start()
    Procc_1.join()
    print(RESULT)

출력:

Result: [25, 36, 49, 64]
[]

그래서, 이것에 대한 해결책은 무엇입니까? 하지만 먼저 어떻게 해결할 수 있는지 봅시다.

다중 처리 간의 데이터 공유 문제를 해결하는 솔루션

이 섹션에서는 변경 사항의 가치를 얻고 다중 처리 간에 데이터를 공유하는 문제를 해결하는 데 도움이 되는 솔루션을 볼 것입니다.

이 솔루션을 공유 메모리라고 합니다. multiprocessing 모듈은 프로세스 간에 데이터를 공유하는 데 사용할 수 있는 ArrayValue라는 두 가지 유형의 객체를 제공합니다.

배열은 공유 메모리에서 할당된 배열입니다. 기본적으로 컴퓨터 메모리에는 공유 메모리 또는 여러 프로세스가 액세스할 수 있는 영역이라고 부를 수 있는 부분이 있습니다.

그래서 그 공유 메모리에서 우리는 새로운 배열이나 새로운 값을 생성합니다. 이러한 추가 값은 기본 Python 데이터 구조가 아닙니다. multiprocessing 모듈 자체에 뭔가 다른 정의가 있습니다.

이제 multiprocessing.Array()를 사용하여 RESULT_ARRAY라는 객체를 선언합니다. 그런 다음 이 배열에서 데이터 유형을 전달해야 합니다. i를 문자열로 전달합니다. 즉, 그 안에 정수 값을 넣고 size를 지정해야 합니다.

예제 코드:

RESULT_ARRAY = multiprocessing.Array("i", 4)

크기를 동시에 줄 수 있도록 C 프로그래밍 스타일 배열과 관련이 있습니다. 이렇게 하면 원하는 위치에 개체를 저장할 수 있습니다.

이제 OBJ_Sum이라는 새 값을 생성하고 있으며 multiprocessing.Value()와 동일하며 값을 저장하고 입력합니다.

예제 코드:

OBJ_Sum = multiprocessing.Value("i")

다음으로 procc_1이라는 개체를 생성합니다. 이 개체는 multiprocessing.Process()와 동일하며 이를 함수라고 합니다. 우리는 Make_Sqaured_List()라는 함수를 만들었습니다. 이 함수는 다음 세 가지 인수를 사용합니다.

  1. 목록
  2. 배열 객체
  3. 가치 객체.

args라는 Process 인수를 사용하여 이 세 가지 인수를 함수에 전달합니다. 예를 들어 다음 코드 펜스를 살펴보십시오.

예제 코드:

Procc_1 = multiprocessing.Process(
    target=Make_Sqaured_List, args=(Items_list, RESULT_ARRAY, OBJ_Sum)
)

이제 Make_Sqaured_List() 함수에서 enumerate() 함수를 사용하여 Items_list를 반복할 것입니다. Items_listindexvalue를 얻을 수 있습니다.

C 스타일 배열이므로 인덱싱을 사용하여 배열에 값을 할당해야 합니다. 또한 배열의 값을 합산할 것이며 OBJ_Sum.valuemultiprocessing.Value()의 속성입니다.

예제 코드:

def Make_Sqaured_List(Items_list, RESULT, OBJ_Sum):

    for i, n in enumerate(Items_list):
        RESULT[i] = n ** 2

    OBJ_Sum.value = sum(RESULT)

메인 프로세스에서 몇 가지 변수를 정의하여 하위 프로세스에서 호출하는 함수를 변경했습니다. 따라서 우리의 주요 의제는 메인 프로세스에서 변경된 값을 얻을 수 있는지 여부입니다.

이제 자식 프로세스에 반영된 배열에 액세스하고 OBJ_Sum.value를 사용하여 합계를 얻을 수 있습니다. 예를 들어 다음 코드 스니펫을 참조하십시오.

예제 코드:

import multiprocessing


def Make_Sqaured_List(Items_list, RESULT, OBJ_Sum):

    for i, n in enumerate(Items_list):
        RESULT[i] = n ** 2

    OBJ_Sum.value = sum(RESULT)


if __name__ == "__main__":
    Items_list = [5, 6, 7, 8]

    RESULT_ARRAY = multiprocessing.Array("i", 4)
    OBJ_Sum = multiprocessing.Value("i")

    Procc_1 = multiprocessing.Process(
        target=Make_Sqaured_List, args=(Items_list, RESULT_ARRAY, OBJ_Sum)
    )
    Procc_1.start()
    Procc_1.join()
    print(RESULT_ARRAY[:])
    print(OBJ_Sum.value)

출력:

[25, 36, 49, 64]
174

이렇게 하면 부모 프로세스에 정의된 개체와 자식 프로세스에서 반환되는 변경 사항을 변경할 수 있습니다. 이는 공유 메모리 기술을 사용하여 가능합니다.

multiprocessing.Lock()을 사용하여 Python에서 공유 리소스 잠금

잠금이라는 중요한 주제를 다룰 것입니다. 이제 컴퓨터 과학이나 운영 체제 수업을 들었다면 이미 잠금에 대해 배운 것입니다. 그러나 잠금은 다중 처리 및 운영 체제 개념과 관련하여 중요한 개념입니다.

먼저 실생활에서 자물쇠가 필요한 이유를 살펴보겠습니다. 일상 생활에서 일부 리소스는 두 사람이 동시에 액세스할 수 없습니다.

예를 들어, 욕실 문에는 “잠금 장치"가 있습니다. 두 사람이 동시에 접근하려고 하면 매우 난처한 상황이 발생하기 때문입니다. 그래서 우리는 자물쇠로 공유 리소스인 욕실을 보호합니다.

마찬가지로 프로그래밍 세계에서 두 개의 프로세스나 스레드가 공유 메모리 파일이나 데이터베이스와 같은 공유 리소스에 액세스하려고 할 때마다. 문제를 일으킬 수 있으므로 잠금으로 해당 액세스를 보호해야 합니다.

프로그램에 보호 기능을 추가하지 않으면 어떤 일이 발생하는지 예제를 실행하여 확인할 수 있습니다. 다시 말하지만 이것은 뱅킹 소프트웨어 프로그램이며 여기에는 두 가지 프로세스가 있습니다.

첫 번째 프로세스는 MONEY_DP() 함수를 사용하여 은행에 돈을 입금하는 것이고 두 번째 프로세스는 MONEY_WD() 함수를 사용하여 은행에서 돈을 인출하는 것입니다. 그리고 마지막으로 최종 잔액을 인쇄하고 있습니다.

MONEY_DP 섹션에서 200$ 달러 잔액으로 시작합니다. 우리는 100$를 예치하고 있으며 100번 추적한 for 루프가 있으며 반복할 때마다 은행 계좌에 01$를 추가합니다.

마찬가지로 MONEY_WD 함수에서 동일한 루프가 100번 반복되고 매번 은행 계좌에서 1달러가 차감됩니다.

예제 코드:

import multiprocessing
import time


def MONEY_DP(B):
    for i in range(100):
        time.sleep(0.01)
        B.value = B.value + 1


def MONEY_WD(B):
    for i in range(100):
        time.sleep(0.01)
        B.value = B.value - 1

이제 우리는 이전 섹션에서 이미 배운 value라는 공유 메모리 변수를 사용하고 있습니다. 이 multiprocessing 값은 공유 메모리 리소스이므로 이 프로그램을 실행하려고 할 때 어떤 일이 발생하는지 봅시다.

if __name__ == "__main__":
    B = multiprocessing.Value("i", 200)
    Deposit = multiprocessing.Process(target=MONEY_DP, args=(B,))
    Withdrawl = multiprocessing.Process(target=MONEY_WD, args=(B,))
    Deposit.start()
    Withdrawl.start()
    Deposit.join()
    Withdrawl.join()
    print(B.value)

우리는 그것을 여러 번 실행할 것이고 매번 다른 값을 인쇄할 것입니다. 그러나 200$를 인쇄해야 합니다.

출력:

# execution 1
205
# execution 2
201
# execution 3
193

왜 이런 일이 발생합니까? 주로 이 프로세스가 공유 메모리에서 B.value라는 변수를 읽으려고 할 때 발생합니다.

B.value200$ 값이 있다고 가정해 보겠습니다. 값을 읽고 1을 더한 다음 같은 것을 같은 변수에 다시 넣습니다.

B.value200$이고 운영 체제 수준에서 이 추가 작업을 수행하므로 운영 체제 수준에서 여러 어셈블리 라인 명령을 실행하게 됩니다.

그래서 변수 200을 읽었습니다. 1이 추가되고 201B.value 변수에 다시 할당합니다.

예제 코드:

B.value = B.value + 1

이제 동시에 수행하는 동안 이 명령도 MONEY_WD() 함수에서 실행되었습니다.

예제 코드:

B.value = B.value - 1

먼저 입금한 다음 인출하지만 프로세스가 B.value를 읽으려고 하면 입금 프로세스가 원래 변수에 다시 쓰지 않았기 때문에 여전히 200입니다.

MONEY_WD 프로세스 내에서 B.value201로 읽는 대신 B.value200으로 읽고 1을 줄이면 199가 됩니다.

그것이 우리가 일관성 없는 행동을 하는 이유입니다. 먼저 잠금을 사용하여 액세스를 잠급니다. 이제 lock이라는 변수를 만들고 multiprocessing 모듈을 사용하여 Lock 클래스를 사용합니다.

예제 코드:

lock = multiprocessing.Lock()

이제 우리는 잠금을 두 프로세스에 전달하고 두 프로세스 내에서 lock.acquire()를 호출하여 잠금을 넣은 다음 잠금을 해제하기 위해 잠금을 호출합니다. release() 함수.

이러한 잠금 기능은 중요 섹션이라고 하는 공유 리소스에 액세스하는 동안 코드 섹션을 보호합니다.

예제 코드:

import multiprocessing
import time


def MONEY_DP(B, lock):
    for i in range(100):
        time.sleep(0.01)
        lock.acquire()
        B.value = B.value + 1
        lock.release()


def MONEY_WD(B, lock):
    for i in range(100):
        time.sleep(0.01)
        lock.acquire()
        B.value = B.value - 1
        lock.release()


if __name__ == "__main__":
    B = multiprocessing.Value("i", 200)
    lock = multiprocessing.Lock()
    Deposit = multiprocessing.Process(target=MONEY_DP, args=(B, lock))
    Withdrawl = multiprocessing.Process(target=MONEY_WD, args=(B, lock))
    Deposit.start()
    Withdrawl.start()
    Deposit.join()
    Withdrawl.join()
    print(B.value)

이제 이 코드는 매번 200을 인쇄합니다.

출력:

200
PS C:\Users\Dell\Desktop\demo> python -u "c:\Users\Dell\Desktop\demo\demo.py"
200
PS C:\Users\Dell\Desktop\demo> python -u "c:\Users\Dell\Desktop\demo\demo.py"
200
Salman Mehmood avatar Salman Mehmood avatar

Hello! I am Salman Bin Mehmood(Baum), a software developer and I help organizations, address complex problems. My expertise lies within back-end, data science and machine learning. I am a lifelong learner, currently working on metaverse, and enrolled in a course building an AI application with python. I love solving problems and developing bug-free software for people. I write content related to python and hot Technologies.

LinkedIn

관련 문장 - Python Error