728x90
C#
Environment.ProcessorCount
- C#에서 현재 시스템의 논리 프로세서(코어) 수를 반환하는 속성이다.
- 이 속성은 .NET의 System 네임스페이스에 포함되어 있으며, 멀티 스레딩 또는 병렬 작업을 최적화할 때 유용하게 사용할 수 있다.
논리 프로세서와 물리 프로세서
- 논리 프로세서
- 하이퍼스레딩 기술과 같은 기술을 통해 운영 체제가 인식하는 프로세서의 수를 나타낸다.
- 물리 프로세서
- 실제로 존재하는 코어의 수를 나타낸다.
활용
- 스레드 풀 크기 결정
- 시스템의 논리 프로세서 수를 기반으로 스레드 풀의 크기를 동적으로 결정할 수 있다.
- 병렬 작업 최적화
- 데이터 병렬 처리를 최적화할 때 사용할 스레드의 수를 조정할 수 있다.
- 시스템 정보 제공
- 애플리케이션에서 시스템의 프로세서 개수를 사용자에게 표시하거나 로그에 기록할 수 있다.
주의 사항
- Environment.ProcessorCount는 논리 프로세서의 수를 반환하므로, 하이퍼 스레딩이 활성화된 시스템에서는 물리적 코어의 수보다 큰 값을 반환할 수 있다.
- 프로세서의 수를 기반으로 작업을 분배할 때는 CPU 사용률, I/O 대기 시간 등을 고려하여 적절한 병렬 작업의 수준을 결정하는 것이 중요하다.
Static extern
- 외부에서 구현된 정적 메서드를 선언하는 데 사용된다. 이 키워드 조합은 주로 플랫폼 호출을 통해 네이티브 코드(예: C/C++ 라이브러리의 함수)를 호출할 때 사용된다.
- extern 키워드는 메서드의 구현이 외부에 있다는 것을 나타내며, static 키워드는 해당 메서드가 클래스의 인스턴스와 독립적임을 의미한다.
- 보통 DLL(동적 링크 라이브러리)에 있는 함수를 호출하기 위해 사용된다. DllImport 속성을 사용하여 해당 함수가 포함된 DLL을 지정해야 한다.
중요 사항
- 플랫폼 호출(P/Invoke)
- extern 키워드는 메서드의 구현이 관리되지 않는 코드(네이티브 코드) 에서 제공됨을 의미한다.
- P/Invoke는 C# 코드에서 네이티브 DLL의 함수를 호출할 때 사용된다.
- 메서드의 반환값과 매개변수
- 네이티브 함수의 반환값과 매개변수를 정확하게 일치시키는 것이 중요하다.
- 잘못된 선언은 런타임 오류를 초래할 수 있다.
- 메모리 관리
- 네이티브 코드와 상호 작용할 때는 메모리 관리에 주의해야 한다. 잘못된 포인터 사용 또는 메모리 해제는 충돌을 일으킬 수 있다.
- 플랫폼 종속성
- P/Invoke는 주로 Windows 플랫폼에서 사용되며, 플랫폼 종속적인 코드가 될 수 있다. 다른 운영체제에서 사용할 때는 주의해야 한다.
MethodImpl
- Methodlmpl 특성(attribute)은 .NET에서 메서드의 구현 방식이나 특정 기능을 지정하는 데 사용된다. 이 특성은 주로 성능 최적화, 동기화, 인라인(inline) 규칙 등을 제어하기 위해 사용된다.
- MethodImplAttribute는 System.Runtime.CompilerServices 네임스페이스에 정의되어 있으며, 메서드, 생성자 또는 속성 접근자에 적용할 수 있다.
주요 MethodImplOptions 값
- NoInlining
- 메서드가 JIT(Just-In-Time) 컴파일러에 의해 인라인되지 않도록 한다. 인라인은 메서드 호출을 메서드 본문으로 대체하는 최적화 기술이다.
- AggressiveInlining
- JIT 컴파일러가 가능하면 메서드를 인라인하려고 시도하도록 한다. 이는 성능을 극대화하기 위해 사용될 수 있다.
- Synchronized
- 메서드에 대한 접근이 스레드 안전하게 보장된다. 해당 메서드가 호출될 때 자동으로 lock(this)와 같은 방식으로 동기화된다. 그러나 성능 저하와 교착 상태(deadlock)의 위험이 있으므로 일반적으로 권장되지 않는다.
- InternalCall
- 메서드 구현이 네이티브 코드에 있으며, 런타임에 의해 호출될 때 실행된다. 일반적으로 CLR의 내부 메서드에 사용된다.
- ForwardRef
- 메서드가 나중에 제공될 것임을 나타낸다. 메서드의 실제 구현은 제공되지 않으며, 주로 런타임이 이 기능을 사용할 수 있다.
- PreserveSig
- 메서드의 서명이 그대로 유지되도록 지정한다. COM 상호 운용 시, HRESULT 반환 값을 유지하기 위해 사용된다.
주의 사항
- MethodImplOptions.Synchronized는 모든 메서드 호출에 대해 잠금을 설정하므로, 동시성 문제를 해결할 수 있지만 성능에 부정적인 영향을 미칠 수 있다. 가능하면 lock문을 사용하여 명시적인 잠금 관리가 더 바람직하다.
- AggressiveInlining 및 NoInlining은 JIT 컴파일러에 대한 힌트일 뿐, 반드시 이러한 방식으로 컴파일되도록 보장하지는 않는다.
bool 함수<TEnum>(this TEnum, params TEnum[])
- 제네릭 타입 매개변수
- <TEnum>은 제네릭 타입 매개변수이다. 이는 함수가 특정 타입에 대해 동작함을 나타낸다.
- 확장 메서드
- this TEnum은 이 함수가 확장 메서드임을 나타낸다.
- 이는 TEnum 타입의 객체에 대해 이 메서드를 직접 호출할 수 있게 해준다.
- 가변 길이 매개변수
- params TEnum[]는 가변 길이 매개변수 배열이다.
- 이 함수를 호출할 때 0개 이상의 TEnum 타입 인자를 전달할 수 있다.
- params TEnum[] 를 통해 Enum 값들의 배열을 받을 수 있으며, 이는 리스트와 유사하게 동작할 수 있다.
확장 메서드(Extension Method)
- 기존 타입에 새로운 메서드를 추가할 수 있게 해준다. 이는 원본 타입의 소스 코드를 수정하지 않고도 새로운 기능을 추가할 수 있는 강력한 방법이다.
기본 개념
- 정적 클래스 내에 정의된다.
- 첫 번째 매개변수에 this 키워드를 사용한다.
- 이 첫 번째 매개변수는 확장하고자 하는 타입을 나타낸다.
문법
public static class ExtensionClass
{
public static ReturnType MethodName(this ExtendedType variable, OtherParameters)
{
// 메서드 구현
}
}
특징
- 원본 타입을 수정하지 않고 새 기능을 추가할 수 있다.
- 인스턴스 메서드처럼 호출할 수 있다.
- 네임스페이스를 통해 확장 메서드의 가시성을 제어할 수 있다.
사용 사례
- 기본 .NET 타입에 유틸리티 메서드 추가
- 인터페이스에 기본 구현 제공
- 제네릭 타입에 대한 확장 기능 구현
장점
- 코드의 가독성과 유지보수성 향상
- 기존 코드를 변경하지 않고 새 기능 추가 가능
- 인터페이스에 대한 확장 가능
주의사항
- 남용하면 코드가 복잡해질 수 있다.
- 확장 메서드와 인스턴스 메서드 간의 이름 충돌에 주의해야 한다.
- 확장 메서드는 정적 바인딩을 사용하므로 다형성을 지원하지 않는다.
제약사항
- 확장 메서드는 static 클래스에서만 정의할 수 있다.
- 확장 대상 타입의 private 멤버에는 접근할 수 없다.
Dictionary<TKey, TValue>
- 고유한 키를 기반으로 값을 저장하는 컬렉션이다.
특징
- 고유한 키
- Dictionary에서 각 키는 고유해야 한다. 동일한 키를 여러 번 추가하려고 하면 ArgumentException 예외가 발생한다.
- 값 덮어쓰기
- 이미 존재하는 키에 대해 값을 설정하면, 해당 키의 기존 값이 새로운 값으로 대체된다.
- 이는 myDictionary[1] = “Updated First”; 와 같은 방식으로 수행할 수 있다.
- 예외 처리
- Add 메서드를 사용하여 중복된 키를 추가하려고 하면 ArgumentException이 발생하므로, 이를 처리할 필요가 있다.
- 확인 및 제거
- 특정 키가 존재하는지 확인하려면 ContainsKey 메서드를 사용한다.
- 키 - 값 쌍을 제거하려면 Remove 메서드를 사용한다.
Google.Protobuf IDeepClonealbe<T>
- 프로토콜 버퍼스(Protocol Buffers, Protobuf) 메시지 객체를 깊은 복사(deep copy)하는 기능을 제공하는 인터페이스이다. 이는 Protobuf를 사용하여 정의된 메시지 타입에서 주로 사용된다.
- Protobuf는 구글에서 개발한 언어 중립적인 데이터 직렬화 라이브러리이다.
사용 목적
- 깊은 복사
- 깊은 복사는 객체의 복사본을 만들 때, 원래 객체의 모든 필드 값을 복사하고 그 필드들이 참조하는 객체들 역시 모두 복사하여, 원래 객체와 독립적인 새로운 객체를 만드는 작업을 말한다. 이는 얕은 복사(참조만 복사)와 대비된다.
- 불변성 유지
- Protobuf 메시지 객체는 불변성을 유지하는 것이 일반적이다. 원본 객체의 상태를 변경하지 않고, 수정된 복사본을 생성하기 위해 깊은 복사가 필요할 수 있다.
- 데이터 변형 방지
- 데이터를 복사하여 원본을 보호하고, 복사본을 사용하여 작업을 수행함으로써 데이터 변형을 방지한다.
주의사항
- 성능
- 깊은 복사는 원래 객체와 동일한 구조의 새로운 객체를 생성하므로, 큰 객체를 깊은 복사할 때는 성능에 주의해야 한다.
- 복사본의 독립성
- 깊은 복사된 객체는 원본 객체와 독립적이므로, 한 쪽에서의 변경이 다른 쪽에 영향을 미치지 않는다. 이는 불변성을 유지하는 데 유용하다.
ConcurrentDictionary.TryAdd 메서드
- 새로운 키-값 쌍을 추가할 때 사용한다.
public bool TryAdd(TKey key, TValue value);
- key: 추가할 항목의 키이다.
- value: 추가할 항목의 값이다.
- 반환값: bool 타입의 값으로, 추가 작업이 성공했는지 여부를 나타낸다.
동작 방식
- TryAdd 메서드는 ConcurrentDictionary에 새로운 항목을 추가하려고 할 때, 해당 키가 이미 존재하는지 검사하고, 존재하지 않는 경우에만 항목을 추가한다.
- 키가 존재하지 않는 경우
- 키-값 쌍이 사전에 추가된다.
- TryAdd 메서드는 true를 반환한다.
- 키가 이미 존재하는 경우
- 기존 항목이 변경되지 않는다.
- TryAdd 메서드는 false를 반환한다.
- 키가 존재하지 않는 경우
주요 특징 및 주의점
- 스레드 안정성
- ConcurrentDictionary는 여러 스레드가 동시에 항목을 추가하거나 수정할 수 있는 상황에서도 안전하게 작동한다.
- 내부적으로 적절한 락킹 메커니즘을 사용하여 데이터 일관성을 보장한다.
- 원자성
- TryAdd 메서드는 원자적으로 실행된다. 즉, 다른 스레드가 동시에 사전에 항목을 추가하려고 해도 이 메서드는 항목이 성공적으로 추가되었는지 여부를 정확하게 판단할 수 있다.
- 성능
- ConcurrentDictionary는 동시성 시나리오에서 높은 성능을 제공한다. 단일 스레드에서의 Dictionary와 비교할 때 약간의 성능 오버헤드가 있을 수 있지만, 멀티스레드 환경에서의 안전성과 효율성을 보장한다.
ConcurrentDictionary.AddOrUpdate(value1, value2, (k,v) ⇒ value2) 메서드
- AddOrUpdate는 키가 존재하지 않으면 새 항목을 추가하고, 이미 존재하면 값을 업데이트하는 메서드이다.
- value1
- 추가하려는 키이다
- value2
- 키가 존재하지 않을 때 추가될 새로운 값이다.
- (k, v) ⇒ value2
- 키가 이미 존재할 때 실행되는 람다 함수이다.
동작
- 만약 value1 키가 딕셔너리에 없다면, (value1, value2) 쌍을 새로 추가한다.
- 만약 value1 키가 이미 존재한다면, 해당 키의 값을 value2로 업데이트 한다.
728x90
'Study > TIL(Today I Learned)' 카테고리의 다른 글
24.08.02 C#, DEV (0) | 2024.08.02 |
---|---|
24.08.01 C#, 데이터베이스 샤딩 (0) | 2024.08.01 |
24.07.30 C#, 배치 파일과 도스 명령어, 게임 서버 (1) | 2024.07.30 |
24.07.29 C#, 게임 서버 (0) | 2024.07.29 |
24.07.26 C# (0) | 2024.07.26 |