'상수'에 해당되는 글 2건

  1. 2020.06.08 [C#] Class, 클래스, 구조체
  2. 2020.05.28 [Developer] 전역변수

Class...객체지향에서 빼놓을 수 없는.

Class에는 항상 상속, 오버로딩, 오버라이딩 그리고 캡슐화가 항상 붙어 다닌다.

 

절차적 언어인 C언어를 주 언어로 사용하던 나는

솔직히 객체지향 방식이 쉽게 와닿지 않았다. (사실은 지금도 그렇다....)

그래서 Python을 써도 C언어를 주로 쓰던 시절처럼 Library 함수 형태로 만드는 것이 익숙하고

Class 형식의 Library를 만드는 것이 덜 익숙하다. 

 

그래도 Class에 대해 정리를 해두면 필요할 경우 곧잘 사용할 수 있겠지. 

 

C언어를 사용하던 나에게는 구조체(Struct)라는 개념은 아주 많이 익숙하다. 

때때로 C# 책을 만든 저자들은 클래스와 구조체를 크게 구분하지 않으려 하지만 나와 같은 개발자에게는

분명하게 개념상의 차이점이 있다. 

 

  • 클래스 및 구조체에는 데이터와 동작을 나타내는 멤버가 있다. (사실 8년여간 C 프로그래밍을 해오면서 단 한번도 구조체 않에 멤버 함수를 선언해서 사용해 본 적이 없다!!)
  • 클래스가 상속 받았다면, 클래스에서 선언된 모든 멤버와 함께 상속받은 클래스에서 선언된 모든 멤버들(생성자 및 소멸자 제외)도 클래스의 멤버에 포함된다.
멤버 설명
필드 필드는 클래스 또는 구조체에서 직접 선언되는 모든 형식의 변수이다. 필드는 기본 제공 형식 또는 다른 클래스의 인스턴스일 수 있다.
상수 상수는 값이 컴파일 시간에 설정되며, 변경할 수 없는 필드나 속성이다.
속성 해당 클래스의 필드처럼 액세스되는 클래스의 메소드로 객체 모르게 필드가 변경되지 않도록 할 수 있다.
메소드 클레스가 수행하는 작업을 정의한다. 입력으로 매개변수를 사용할 수 있고, 매개변수를 통해 출력 데이터를 반환할 수 있다. 매개변수를 통하지 않고 직접 값을 반환할 수도 있다.
이벤트 이벤트는 버튼 클릭, 성공적인 메소드 완료 등의 사건이 발생했을 때 알림을 다른 객체에 제공한다.
이벤트는 대리자(delegate)를 사용하여 정의 및 트리거 된다.
연산자 오버로드 된 연산자는 클래스 멤버로 간주되며, 클래스에서 'public static' 메소드로 정의한다.
인덱서 인덱서를 사용하면 배열과 유사한 방식으로 객체를 인덱싱할 수 있다. 
생성자 생성자는 객체를 처음 만들 때 호출되는 메소드이다. 보통 객체의 데이터를 초기화하는데 사용한다.
소멸자 C#에서 드물에 사용되며, 메모리에서 객체를 제고할 때 런타임 실행 엔진이 호출하는 메소드이다.

 

C# 공부를 위해 구매한 책에서 발췌한 내용이다. 

다른 것들은 한번쯤은 들어보거나 사용해봤던 것들인데, '이벤트'와 '인덱서'는 조금 새롭다. 

 

C 언어로 프로그래밍 할 때 '구조체 배열'을 자주 사용했었는데, '인덱서'를 사용하면 비슷한 기능을 구현할 수 있을 것 같다. 

'이벤트' 요소는 .NET 계열에서 특화적으로 사용되는 것으로 생각되는데(그냥 내 생각....), 내가 구현하고자 하는 Trading System에서 아주 많이 사용될 기능으로 보인다. 

 

  1. 필드 (Field)
  • 인스턴스(객체) 필드 : 객체 이름과 함께 사용. public string name과 같이 [접근제한자][자료형][필드명]으로 생성하고 [인스턴스].[필드명]으로 사용.
  • 정적(클래스) 필드 : 클래스 이름과 함께 사용. static 키워드 필수. [클래스이름].[필드명]
  1. 상수
  • 컴파일 시간에 알려진 변경할 수 없는 값. 프로그램 수행되는 공안 갑승ㄹ 변경하지 않는 경우 사용.
  • const 한정자 사용.
  • bool, byte, char, int double, string만 const 사용 가능.
  • Access : [클래스이름].[상수명]

 

 

Posted by [ 브랜든 ]
,

[Developer] 전역변수

Developer 2020. 5. 28. 22:57

Simple한 얘기를 해보자. 

 

  • 전역 변수 - 파일 내 어디서든 접근 가능하며, 변하는 값(변수)

엄청나게 유용해 보인다. 소스 파일 내의 어디서는 접근이 가능하다니!! 

하지만

'변수'라는 상태가 문제다.

소스 파일내 어디서든 이 값을 변경할 수 있다는 것이다. 

 

만약 프로그램이 One Thread의 순차처리 방식이라면 당연히 문제가 없겠지만

Multi Thread일 경우 누가 어느 시점에 이 값을 바꿀지 모른다.

 

 

[ 프로그램 동작 전제 ]

  1. Non-Block Packet Receive로 지속적으로 메시지를 수신하여 동일 로직을 처리한다.
  2. 사용자는 1개의 세션을 통해 연결 후, 모든 Transaction이 끝날 때까지(즉, End 메시지를 전송할 때까지) 메모리에 데이터를 유지한다.
  3. 세션관리자는 사용자의 Transaction 진행 여부를 알 수 있다.
  4. 세션 연결 후, 첫 메시지 처리 과정에 사용자 정보를 구조체 배열 메모리(Context Index 기준)에 저장 후, 두 번째 이후 메시지 부터는 사용자 번호를 기준으로 Context Index를 가져와 사용자 정보를 조회/갱신 한다.
  5. Transaction 종료 시, 구조체 배열 메모리 및 세션 관리에서 데이터를 삭제한다.

위의 케이스는 전에 다니던 회사에서 겪은, 실제 운영 시스템에서 발생한 장애 상황을 심플하게 줄여서 정리한 것이다. (실제로는 훨씬..무지막지하게 복잡하다...)

한 시점에 여러명의 사용자가 지속적인 Transaction 처리 중인 상태가 될 수 있기 때문에, Non-Block 상태에서 여러 Transaction을 처리하는 프로그램의 세션, Context 관리란 정말 골치 아프다. 

 

A 사용자가 Transaction을 진행하고 있다. a라는 메시지를 시작으로 30번의 Context ID를 할당받고, b라는 메시지를 전달했을 때, 세션관리자가 A 사용자라는 것을 인지하고 30번의 Context ID를 알려주어 아주 빠른 메모리 접근으로 작업을 처리하도록 했다.

문제는 세션관리자가 Context ID를 'Global Index'에 넣어주고 Process #1은 이 값으로 메모리에 접근하게 되어 있었다는 것이다. (아마 처음 이 프로그램을 만든 사람은...a라는 메시지를 처리하는 함수와 b라는 메시지를 처리하는 메시지가 별도의 파일로 관리되고 있어 전역 변수를 선언해서 extern 명령어로 어디서든 접근하기 쉽게 하려고 했던것 같다.)

 

만약 A 사용자가 a 메시지를 처리하고 b 메시지를 전달후 b 메시지에 대한 함수를 타기 바로 전에 B 사용자가 전송한 새로운 a 메시지가 먼저 처리되면...? 

A 사용자의 b 메시지를 처리하기 바로 직전에 Global Index는 A 사용자의 Index 30에서 B 사용자의 Index 25로 변경될 것이다. 그럼 A 사용자의 b 메시지를 처리할 때는 당연히 Index 25에 접근하게 되어 잘못된 데이터를 처리하게 될 것이다.

 

이것이 얼마나 심각한 문제일지는 겪어본 사람은 알겠지...

거대한 운영 시스템에서 원인 분석만 꼬박 일주일을 잡아 먹은적도 있을 정도다. 

장애 이후 전역변수를 없애는 일 또한 엄청난 곤욕이다.

 

오늘부로 아래 3가지만 실천해 보자.

  1. 모든 전역'변수'는 제거한다.
  2. 변수는 반드시 함수의 Parameter(인자)로 넘겨줘서 사용한다. Call-by-Value든 Call-by-Reference든 상관없다.
  3. 전역으로 선언할 수 있는 것은 오직, 값을 변경할 수 없는 '상수'뿐이란 것을 명심하자.

 

다음에는 위 문제로 새로 만들었던 'Context 관리 라이브러리'를 포스팅 해봐야겠다.

'Developer' 카테고리의 다른 글

[Developer] Input Validation - 입력값 검증  (0) 2020.05.26
[Developer] 개발할 때가 좋았는데.....  (0) 2020.05.25
Posted by [ 브랜든 ]
,