그럼에도 잠금을 해제하고 로컬 변수가 가리키는 주소 값을 읽고 쓰게 되면 데이터 레이스로 이어집니다. 즉, 버그가 발생하는 것인데 그럴 수밖에 없습니다. 목록 자체를 액세스하는 것이 아니더라도 로컬 변수가 목록의 무언가를 가리키고 있기 때문입니다.
잠금으로 보호되는 변수 x의 일부를 로컬 변수로 가리키는 변수 y가 존재할 때(로컬이든 다른 객체의 멤버 변수든), 변수 y가 변수 x를 액세스하려면 변수 x에 대한 잠금을 해제하지 말아야 합니다. 다음 예시에서 ➊이라고 표기된 곳에서는 잠금 해제를 하지 말아야 합니다.
class Player { int x; int y; }; mutex mutex; List<Player*> playerList; void func() { lock(mutex); Player* p = playerList.GetPlayer("John"); // ➊ p-> ...; p-> ...; }
잠금의 전염성은 소스 코드를 보아도 쉽게 눈에 띄지 않을 때가 있습니다. 멀티스레드 프로그래밍에서 특히 주의해야 하는 부분이기도 합니다. 역시나 프로그램 규모가 커지면 잠금의 전염성은 항상 여러분을 신경 쓰게 만들 것입니다.
Note ≣ 합성 가능성의 부족(lack of composability)이란?
예를 들어 송금하는 프로그램이 사용자 A의 변수를 차감하고 사용자 B의 변수를 차감한 만큼 증가시키는 경우가 있습니다. 설령 사용자 A가 뮤텍스 잠금으로 안전하게 보호되고 있고, 사용자 B도 마찬가지로 보호되고 있다 하더라도, 송금을 하는 프로그램이 동시에 둘 이상의 스레드에서 실행될 때 송금 프로그램은 오작동을 할 수 있습니다. 이러한 문제를 해결하려면 사용자 A와 사용자 B를 한꺼번에 모두 보호하는 뮤텍스 잠금을 하든지, 혹은 사용자 A와 사용자 B의 잠금을 모두 한 후에 송금 처리를 해야 합니다. 즉, 프로그램 개발이 기대 이상으로 복잡해집니다. 이렇게 여러 모듈이나 로직을 쉽게 조합할 수 없는 것을 합성 가능성의 부족이라고 합니다.