소프트웨어 공부/프로그래밍

안전한 공유를 위한 코딩의 시기와 방식을 결정하라.

야곰야곰+책벌레 2021. 4. 20. 12:59
728x90
반응형

애플리케이션이 다중 스레드나 프로세스를 이용한다면, 공유 개체를 최소화할 수 있는 방법과 올바르고 안전한 공유를 위한 시기를 알아야 한다. C++ 표준에는 스레드에 대한 별다른 언급이 없지만 C++는 멀티스레드 코드를 만드는 데 꾸준하게 이용되고 있다. 만약 여러분의 애플리케이션이 스레드 간의 데이터를 공유하고 있다면 반드시 안전한 방법을 강구해야 한다.

 

  • 타깃 플랫폼의 문서를 참조하라
    아주 사소한 정수 연산에서부터 내부 프로세스 및 크로스 프로세스 연산에 이르기까지 로컬 범위의 싱크로 문제에 대해서는 일단 타깃 플랫폼이 어떠한 기능을 갖추고 있는지부터 확인해야 한다.
  • 플랫폼의 기본 기능들을 여러분의 자체적인 추상체에 포함시켜라
    여러 플랫폼 간의 호환이 필요할 때 좋은 방법이다.
    플랫폼이 지원하는 기능을 그대로 쓰지 않고, 추상화하여 제어하는 것이다.

    이 과정을 진행해주는 라이브러리를 사용하는 것도 또 다른 방법이다.
  • 사용하고 있는 타입이 멀티스레드 프로그램에서 안전한지 확인하라
    각 타입에 필요한 조건은 다음과 같다.
  • 공유되지 않는 개체들은 서로 독립적이어야 한다
    두 스레드가 호출자 쪽에서의 특별한 조치 없이도 각각의 개체들을 자유롭게 사용할 수 있어야 한다.
  • 서로 다른 스레드에 있는 해당 타입의 같은 개체를 사용할 때는 호출자가 어떻게 해야 하는지 명시해 두어야 한다
    공유 개체로의 접근이 일련화되어야 하는 타입이 대부분이지만, 그렇지 않은 경우도 있다. 이때는 내부적이건 외부적이건 간에 잠금 메커니즘 등이 필요하므로 주의해야 한다.

멀티스레드 프로그램에서 표준 라이브러리 구성 요소를 사용할 경우는 그 라이브러리의 문서를 통해 어떤 기능이 지원되는지를 확실히 해두어야 한다. 멀티스레드 프로그램에서 사용할 용도로 자체적인 타입을 만들 때에도 이미 설명한 것과 같은 두 가지 작업을 반드시 해주어야 한다. 첫 번째는 잠금 메커니즘 없이 서로 다른 스레드가 그 타입의 개체를 사용할 수 있어야 하고, 두번째는 서로 다른 스레드에서 같은 개체를 사용할 경우 어떤 과정이 필요한지 명시해 두어야 한다.

 

  가장 기본적인 디자인 이슈는 클래스와 클라이언트 사이의 올바른 실행을 위한 역할 분담으로, 사용 가능한 방법은 다음과 같다.

 

외부 잠금
  호출자가 잠금을 제어한다. 이 옵션을 사용할 경우 개체를 사용하는 코드에서는 해당 개체가 스레드 사이에 공유되어 있는지를 확인해야 하고, 만약 그렇다면 모든 개체 사용을 일련 화해야 한다. 문자열 타입의 경우 일반적으로 이 옵션이나 불변 방식을 사용한다.
내부 잠금
  각 개체는 자신으로의 모든 접근을 일련화(Serialization)하고, 모든 공용 멤버 함수를 잠가 호출자가 개체 사용을 일련화(Serialization) 할 필요가 없도록 해주는 방식이다. 생산자/소비자 큐 방식에서 일반적으로 사용하는 옵션으로, 개별적인 멤버 함수의 호출에 대해 적절한 수준의 잠금이 이루어지도록 디자인되는 방식이다. 일반적으로 말하면 다음의 두 가지 사항을 여러분이 알고 있을 때 사용되는 옵션이다.

첫 번째는 해당 타입의 개체가 언제나 스레드 사이에서 공유될 것이라는 확신이 있어야 한다. 두 번째는 각 멤버 함수에 대한 잠금이 대부분의 호출자에 대해 적절한 선택인지 확인해야 하며, 호출자가 하나의 작업이 아닌 여러 작업을 잠가야 하는 경우라면 이 옵션은 적절하지 않다. 쉽게 말해 잠금의 주체가 호출자 위주로 이루어져야 한다면 이 옵션을 사용해서는 안된다. 즉 내부 잠금은 타입의 공용 인터페이스와 밀접한 관계가 있다. 타입의 각 작업이 완전하게 독립적일 때 내부 잠금이 적당하다는 것이다. 타입의 추상화 정도가 보다 정확하게 표현되어야 하고, 캡슐화되어야 한다는 의미이다.

기본 작업을 결합하여 보다 일잔적이고 규모가 큰 작업을 만들 경우에는 이와 같은 내부 잠금이 적절하지 않으므로, 콜백 기반의 모델을 사용하거나 잠금에 대한 정보를 어떤 방법으로든 공개하여 외부에서 제어할 수 있게끔 해주어야 한다.

 

읽기 전용 개체와 같이 잠금과 관계없는 디자인 : 잠금이 필요하지 않게끔 타입을 디자인하는 것도 가능하다. 변하지 않는 개체가 좋은 예로, 변하지 않기 때문에 잠가둘 필요가 없다. 불변의 문자열 타입을 예로 들면 한 번 만들어진 후 변하지 않으므로, 모든 문자열 작업에 의해 만들어지는 문자열은 모두 새로운 것으로 볼 수 있다.

 

  여러분이 널리 용될 라이브러리를 만들 경우에는 개체가 멀티스레드 프로그램에서 안정적으로 작동되게 하되, 싱글 스레드 프로그램에서도 별도의 오버헤드가 없도록 디자인해야 한다. 예를 들어 copy-on-write 방식을 사용하는 타입이 포함된 라이브러리를 만든다면 최소한 내부 잠금 기능이 있어야 하고, 이 기능이 싱글 스레드 빌드에서는 전혀 방해가 안되도록 해주어야 한다. 잠금이 여러 번 일어날 때에는 같은 잠금에 연결된 모든 코드를 정렬하여 데드락이 발생하지 않도록 해야 한다.

 

728x90
반응형