프로세스들은 서로 독립적이지만, 프로세스 간 통신을 하거나 같은 대상에 대한 작업을 함으로써 협력할 수 있다. 그런데 이때, 동시다발적으로 작업을 처리하면 문제가 발생 할 수 있다.
이를 위해 프로세스 동기화가 필요하고, 스레드간에도 이것이 적용된다.
프로세스 간 통신에서는 공동으로 이용하는 변수가 파일, 입출력 기기 등이 존재하고
이를 가리켜 '공유자원'이라 한다.
공유자원은 각 프로세스의 접근 순서에 따라 결과가 달라질 수 있는데, 프로세스가 동시에 실행할 경우 문제가 발생할 수 있는 영역을 가리켜 '임계구역'이라고 한다.
예를들어 프로세스A는 +1시키고, 프로세스B는 -1시킬 때 공유자원에 하나씩 접근하면 다시 0이되지만 동시에 접근하게 되면 +1만이 넣어지거나 -1만이 넣어질 수 있다.

뮤텍스락은 동시에 접근되면 안되는 임계구역에 동시에 접근 할 수 없도록 만드는 도구이다.
임계구역에 진입하는 프로세스는 뮤텍스락을 이용해 임계구역의 문을 잠글 수 있음.
뮤텍스락은 두개의 대표적인 함수를 호출해 동작을 수행함
: - acquire(): 프로세스가 임계구역에 진입하기 전에 호출하는 문잠그기 함수

뮤텍스락은 공유자원이 1개있을 때라 생각하고 만든 동기화 방법이지만,
세마포어는 공유자원이 여러개 있을 때도 사용 가능한 동기화 방법임.
어떤 프로세스가 아무도 이용하지 않는 공유자원1에 접근하려 할 때 운영체제는 세마포어라는 열쇠를 그 프로세스에게 건네주어 공유자원1에 접근가능하게 함.
새로운 프로세스가 와서 공유자원1에 접근하려 할 때 이미 열쇠(세마포어)이용중이니 기다리라고 함
세마포어도 두개의 대표적인 함수를 호출해 동작을 수행함
: - wait(): 프로세스가 임계구역에 들어갈 수 있는지 기다려야하는지 알려주는 함수
<뮤텍스락 걸어주지 않아 생기는 문제 확인>
from multiprocessing import Process, Value
# Value: 프로세스간의 값을 공유하게 만들고 싶을 때 사용하는 파이썬의 생성자함수
def counter1(snum, cnt): # snum:공유하는숫자, cnt:몇번연산을 수행할지
for i in range(cnt):
snum.value += 1
def counter2(snum, cnt): # snum:공유하는숫자, cnt:몇번연산을 수행할지
for i in range(cnt):
snum.value -= 1
if __name__ == "__main__" :
shared_number = Value('i', 0) # int로 만들고, 초기값이 0
p1 = Process(target=counter1, args=(shared_number,5000))
p1.start()
p2 = Process(target=counter2, args=(shared_number,5000))
p2.start()
p1.join()
p2.join()
print("finally, number is", shared_number.value)
<실행결과>

-5000 +5000 하면 0이되야 맞지만, 동기화오류로 625가 나옴.
<뮤텍스 락을 걸어준 코드>
from multiprocessing import Process, Value, Lock
# Value: 프로세스간의 값을 공유하게 만들고 싶을 때 사용하는 파이썬의 생성자함수
def counter1(snum, cnt, lock): # snum:공유하는숫자, cnt:몇번연산을 수행할지
lock.acquire() # 처음들어올 때 락을 걸고
try:
for i in range(cnt):
snum.value += 1
finally:
lock.release() #문제없다면 release 해줌
def counter2(snum, cnt, lock): # snum:공유하는숫자, cnt:몇번연산을 수행할지
lock.acquire() # 처음들어올 때 락을 걸고
try:
for i in range(cnt):
snum.value -= 1
finally:
lock.release() #문제없다면 release 해줌
if __name__ == "__main__" :
lock = Lock()
shared_number = Value('i', 0) # int로 만들고, 초기값이 0
p1 = Process(target=counter1, args=(shared_number,5000, lock))
p1.start()
p2 = Process(target=counter2, args=(shared_number,5000,lock))
p2.start()
p1.join()
p2.join()
print("finally, number is", shared_number.value)
<실행결과>

p1이 실행할 때 p2 실행 안했고, p2 실행시 p1실행 안했음.
<스레드에 대해 뮤텍스 락 제공>
import threading
from multiprocessing import Process, Value, Lock
# Value: 프로세스간의 값을 공유하게 만들고 싶을 때 사용하는 파이썬의 생성자함수
def counter1(snum, cnt, lock): # snum:공유하는숫자, cnt:몇번연산을 수행할지
lock.acquire() # 처음들어올 때 락을 걸고
try:
for i in range(cnt):
snum.value += 1
finally:
lock.release() #문제없다면 release 해줌
def counter2(snum, cnt, lock): # snum:공유하는숫자, cnt:몇번연산을 수행할지
lock.acquire() # 처음들어올 때 락을 걸고
try:
for i in range(cnt):
snum.value -= 1
finally:
lock.release() #문제없다면 release 해줌
if __name__ == "__main__" :
lock = Lock()
shared_number = Value('i', 0) # int로 만들고, 초기값이 0
t1 = threading.Thread(target=counter1, args=(shared_number,10000, lock))
t1.start()
t2 = threading.Thread(target=counter2, args=(shared_number,10000,lock))
t2.start()
t1.join()
t2.join()
print("finally, number is", shared_number.value)
<실행결과>
