'Software Architect/C#'에 해당되는 글 30건
- 2013.07.18 [C#] MS CHART CONTROL 사용방법 가이드 링크 (891)
- 2013.05.23 [C#] C#에서 동영상 재생 프로그램 (2)
- 2013.04.27 [C#][Ribbon] VS2010에서 MDI폼 리본메뉴 만들기 (5)
- 2012.11.09 [C#] Multi-Thread - 멀티스레드 (1)
- 2012.10.18 닷넷 기반에서의 [double / float]의 연산 및 Casting문제와 해결방법 (1)
- 2012.10.16 디자인모드 / 런타임 모드 확인방법
- 2012.09.24 닷넷 기반에서의 [double / float]의 연산 및 Casting문제와 해결방법 (1)
- 2012.01.11 [C#] Volatile 키워드의 의미(자바 자료) (2)
- 2012.01.11 [C#] Lock문 (1)
- 2012.01.11 [C#] Volatile 키워드 (2)
- 2012.01.10 [Log4net] 로깅 레벨 이해하기 - Logging level (1)
- 2012.01.10 [Singleton] 멀티스레드 환경에서의 싱글턴
- 2012.01.10 [LOG] 로그 클래스 설계(LOG CLASS) (1)
- 2012.01.05 [C# + log4net] 손쉬운 로그남기기
- 2012.01.05 [C#] log4net 설정 (1)
- 2012.01.05 [C#] DB 처리 클래스 (Provider) (2)
- 2012.01.04 [C#] 부모폼 자식폼 간의 값 전달 방법 (2)
- 2012.01.04 [C#] 자식폼에서 부모 폼으로 이벤트 전달
- 2012.01.04 Dot Net Event / 이벤트 처리 (1)
- 2012.01.03 02 c# - 데이터 / 변수 / 처리 / 연산자
- 2012.01.03 01 C# 워밍업
- 2011.12.14 C# - 오라클 DB 불러오기
- 2011.11.30 2011.11.30 C# 학습 (1)
- 2011.11.28 2011.11.28 C# 학습
- 2011.11.25 2011.11.24 C# 학습
- 2011.11.21 2011.11.21 c# 학습
- 2011.11.18 2011.11.18 C#학습
- 2011.11.17 2011. 11. 17 C# 학습
- 2011.11.17 2011. 11. 16 C# 학습 chapter03_Class
- 2011.11.15 2011. 11. 15. C# 학습 chapter01 부터 chapter03 page113
원문 : http://blog.lovecoco.net/94
DirectX Software Develpment Kit
위 SDK를 설치한 후에
using Microsoft.DirectX.AudioVideoPlayback;
를 이용하여~ 딱 3줄로.. 동영상 재생가능~~
1. 무작정 Visual Studio 2008을 띄우고 C# WinForm 프로젝트 생성
2. [Add Reference]에서 Microsoft.DirectX.AudioVideoPlayback를 추가해주고(DirectX는 깔려 있어야 함)
3. Form을 더블 클릭하고 다음과 같이 쳐준다.
using Microsoft.DirectX.AudioVideoPlayback;
private void Form1_Load(object sender, EventArgs e){ Video v = Video.FromFile(@"c:\test.avi"); v.Owner = this; v.Play(); }
4. F5를 누른다.
근데 안된다.
[Debug]-[Exceptions]-[LoaderLock]의 Thrown 항목을 꺼준다.

5. F5를 누른다.
[출처] C#으로 동영상 플레이 제작을 위한 필수 항목|작성자 위풍당당
출처 : http://www.codeproject.com/Articles/364272/Easily-Add-a-Ribbon-into-a-WinForms-Application-Cs
Easily Add a Ribbon into a WinForms Application (C#)
|
- 닷넷기반의 언어 혹은 일반적인 프로그래밍 언어에서 '실수 연산'을 다룰 때에는 예상하지 못한
여러가지 다른 결과가 출력 될 수 있습니다. - Casting의 문제
float f = 0.55f; double d = (double)f; double d2 = 0.55; decimal dec = 0.55m; Console.WriteLine(d2 + d2); Console.WriteLine(f + 0.12); Console.WriteLine(d2 + 0.12); |
- 위의 연산 결과는 예측된 결과와 전혀 다른 값을 보여줍니다.
- c# 내부에서 소수의 접미사에 f를 붙이지 않을 경우 자동으로 double로 casting 되어 연산되는 문제와
- float과 double의 casting시 예상하지 못한 결과가 나오는 문제가 있습니다.
- 계산 방식의 문제
- 실수 연산에서 위와같이 정확하지 않은 결과가 나오는 이유는
- 프로그램(컴퓨터)는 사람이 생각하는 계산방식과 다르다는 점에 있습니다.
double a = 11.22; double b = 11.10 + 0.12; Console.WriteLine("{0:e16}", a); Console.WriteLine("{0:e16}", b); |
- 위의 결과와 같이 컴퓨터는 실수를 표현하는 부동 소수점 방식은 특정한 식에 의해
최대한의 근사치 만을 표현하기 떄문에 다른 결과가 나올 수 있습니다.- 돈 혹은 소수점 자리에 민감한 숫자의 경우 실수를 이용한 정확한 연산 결과가 보장되어야 합니다.
- 그러나 부동소수점(float / double)을 이용한 연산 결과는 정확하지 않습니다.
- 그러므로 정확한 소수점 연산이 필요할 시에는 부동소수점을 사용하면 문제가 발생 할 수 있습니다.
- 정확한 계산을 위한 몇 가지 해결 방법
- Decimal
- 정확한 계산이 필요할 시에는 decimal 데이터 타입을 사용하라고 아래와 같이 MSDN은 권고하고 있습니다.
- 'Single 및 Double에서는 정확하지 않는 숫자가 Decimal에서는 정확하게 표시되는 경우가 있습니다(예: 0.2, 0.3). Decimal에서는 부동 소수점에서보다 산술 연산이 더 느리지만 더 정확한 결과가 나타나므로 성능 감소를 감내할 만한 가치가 있습니다'
- 'Decimal 데이터 형식은 모든 숫자 형식 중 가장 느린 형식입니다. 데이터 형식을 선택하기 전에 정밀도와 성능 중 무엇이 더 중요한지를 충분히 검토해야 합니다'
- 단, 위의 문구와 같이 Decimal은 모든 숫자 타입의 형식 중에가서 가장 느리기 때문에
성능과 정확도 중에서 어느 부분을 기준으로 처리해야 할 지 검토해야 합니다.
- 내결함성 허용
- decimal을 사용하지 않고도 실수 연산의 결과가 보장되어야 하는 경우
- 부동 소수점 실수(float/double)를 사용하고 허용하는 범위 내에서 결과를 보장해야 한다라는
내결함성 허용의 조건에 만족한다면 궂이 느린 타입의 decimal을 사용할 필요는 없습니다.
double difference = a * 0.0001; // 0.1%의 오차범위는 허용합니다. bool result = Math.Abs(a - b) <= difference; // true 값으로 반환됩니다. Console.WriteLine(result); Console.WriteLine("{0:e16}", a); Console.WriteLine("{0:e16}", b); |
- Decimal을 사용한 정확한 연산처리 예
- MSDN 참고
- 기타 참고
- 닷넷기반의 언어 혹은 일반적인 프로그래밍 언어에서 '실수 연산'을 다룰 때에는 예상하지 못한
여러가지 다른 결과가 출력 될 수 있습니다. - Casting의 문제
float f = 0.55f; double d = (double)f; double d2 = 0.55; decimal dec = 0.55m; Console.WriteLine(d2 + d2); Console.WriteLine(f + 0.12); Console.WriteLine(d2 + 0.12); |
- 위의 연산 결과는 예측된 결과와 전혀 다른 값을 보여줍니다.
- c# 내부에서 소수의 접미사에 f를 붙이지 않을 경우 자동으로 double로 casting 되어 연산되는 문제와
- float과 double의 casting시 예상하지 못한 결과가 나오는 문제가 있습니다.
- 계산 방식의 문제
- 실수 연산에서 위와같이 정확하지 않은 결과가 나오는 이유는
- 프로그램(컴퓨터)는 사람이 생각하는 계산방식과 다르다는 점에 있습니다.
double a = 11.22; double b = 11.10 + 0.12; Console.WriteLine("{0:e16}", a); Console.WriteLine("{0:e16}", b); |
- 위의 결과와 같이 컴퓨터는 실수를 표현하는 부동 소수점 방식은 특정한 식에 의해
최대한의 근사치 만을 표현하기 떄문에 다른 결과가 나올 수 있습니다.- 돈 혹은 소수점 자리에 민감한 숫자의 경우 실수를 이용한 정확한 연산 결과가 보장되어야 합니다.
- 그러나 부동소수점(float / double)을 이용한 연산 결과는 정확하지 않습니다.
- 그러므로 정확한 소수점 연산이 필요할 시에는 부동소수점을 사용하면 문제가 발생 할 수 있습니다.
- 정확한 계산을 위한 몇 가지 해결 방법
- Decimal
- 정확한 계산이 필요할 시에는 decimal 데이터 타입을 사용하라고 아래와 같이 MSDN은 권고하고 있습니다.
- 'Single 및 Double에서는 정확하지 않는 숫자가 Decimal에서는 정확하게 표시되는 경우가 있습니다(예: 0.2, 0.3). Decimal에서는 부동 소수점에서보다 산술 연산이 더 느리지만 더 정확한 결과가 나타나므로 성능 감소를 감내할 만한 가치가 있습니다'
- 'Decimal 데이터 형식은 모든 숫자 형식 중 가장 느린 형식입니다. 데이터 형식을 선택하기 전에 정밀도와 성능 중 무엇이 더 중요한지를 충분히 검토해야 합니다'
- 단, 위의 문구와 같이 Decimal은 모든 숫자 타입의 형식 중에가서 가장 느리기 때문에
성능과 정확도 중에서 어느 부분을 기준으로 처리해야 할 지 검토해야 합니다.
- 내결함성 허용
- decimal을 사용하지 않고도 실수 연산의 결과가 보장되어야 하는 경우
- 부동 소수점 실수(float/double)를 사용하고 허용하는 범위 내에서 결과를 보장해야 한다라는 내결함성 허용의 조건에 만족한다면 궂이 느린 타입의 decimal을 사용할 필요는 없습니다.
double difference = a * 0.0001; // 0.1%의 오차범위는 허용합니다. bool result = Math.Abs(a - b) <= difference; // true 값으로 반환됩니다. Console.WriteLine(result); Console.WriteLine("{0:e16}", a); Console.WriteLine("{0:e16}", b); |
- Decimal을 사용한 정확한 연산처리 예
- MSDN 참고
- 기타 참고
출처 : http://whiteship.tistory.com/1718
사전적인 의미는 '휘발성의' 라는 뜻이다.
네이버에서 검색한 어떤 블로그를 보니까 "비동기적으로 바뀔 수 있는 변수"로 선언할 때 사용하는 키워드라고 설명되어 있다. ㄷㄷㄷ이다 도무지 감을 못 잡겠다. 구글링을 할 수 밖에 없다. 진작에 구글로 검색할 껄 혹시나 하는
기대감에 네이버로 검색해봤지만, 역시나였다.
건졌다.
나와 비슷한 의문을 가진 사람들이 2005년에도 많이 있었나보다. 3년이 지난 지금에서야 난 좀 이해할 수 있을 것 같다.
자바의 volatile은 멀티 쓰레드 환경에서 "완전히 공유 하겠다"라는 뜻이다.
일반 변수들은 멀티 쓰레드 환경에서 쓰레드 마다 각자 메인 메모리에 위치한 변수 값을 복사하여 그 값을 가지고 논다. 따라서 여러 쓰레드가 그 값들을 변경하면 쓰레드 마다 다른 값을 가지고 있는 경우가 발생할 수도 있다. 그로인한 문제들은 뭐 수도 없으니까 패스하자. 그런 문제들을 해결하는 방법으로 내가 여태까지 알고 있던건 "완전히 분리"하는 방법이었다. 쓰레드 로컬을 쓰던, 아예 로컬 변수로 만들어 버리던 해서 멤버 변수를 사용하지 않는 거였다. 그렇게 해야지 쓰레드 세이프 하라고 하는 건 줄 알고 있었다. 그런데... 완전 반전이다. 그 반대 방법도 있었던 것이다.
모든 쓰레드가 복사본을 가지고 노는게 아니라 메인 메모리에 있는 변수 값을 그대로 사용하고 그 값을 변경시키는 것이다. 이렇게 되면 모든 쓰레드는 동일한 값을 공유하게 된다. 물론 어떤 쓰레드가 변경 시켰는지는 몰겠지만, 중요한 모든 쓰레드가 같은 값(최근에 변경된 값)을 공유한다는 것이다.
그렇다면, 다음의 코드에 대해 잠깐 생각해볼까..
private volatile BundleContext context;
BundleContext를 volatile로 선언했다. 사실 저 코드는 예제에 있던 코드를 무심코 베껴온것이고, 이제사 저 코드의 의미가 궁금해져서 찾아보았다.
private void installNewBundles(File[] bundleFiles) throws BundleException {
for (File file : bundleFiles) {
String bundleLocation = "file:" + file.getAbsolutePath();
if(findBundleByLocation(bundleLocation) == null){
context.installBundle(bundleLocation);
bundleLocations.add(bundleLocation);
}
}
}
protected Bundle findBundleByLocation(String location) {
Bundle[] bundles = context.getBundles();
for (int i = 0; i < bundles.length; i++) {
if (bundles[i].getLocation().equals(location)) {
return bundles[i];
}
}
return null;
}
위 코드들은 모두 쓰레드 안에서 실행하는 메소드들이다. context에 어떤 번들을 설치하고, 가져오는 일들을 하고 있다. 이 코드를 여러 개의 쓰레드가 실행한다고 가정했을 때, 그 여러 개의 쓰레드들이 동일한 BundleContext를 가지고 있지 않다면, 어떤 일이 벌어질까... 상상도 하기 싫을 정도로 끔찍하다. 설치한 걸 또 설치할려고 하거나, 이미 없앤 것을 또 없애려고 할 것이다. 전혀 내가 원한대로 동작하지 않게 된다. 그래서 volatile이 쓰인 것이었다.
나이스... volatile을 이제야 이해했다.
ps : 하지만 "비동기적으로 변경 될 수 있는 값"이라는 말은 아직도 이해가 되지 않는다. 한글이 영어보다 어려운건지. 저 위의 링크에서는 "비동기적으로 변경" 이라는 영어 단어는 볼 적이 없는 것 같은데... 신기할 따름이다.
출처 : MSDN - http://msdn.microsoft.com/ko-kr/library/c5kehkcz(v=VS.90).aspx
lock 키워드는 지정된 개체를 상호 배타적으로 잠그고 문을 실행한 다음 잠금을 해제함으로써 문 블록을 임계 영역으로 표시합니다. 이 문은 다음 형식을 사용합니다.
Object thisLock = new Object(); lock (thisLock) { // Critical code section. }
자세한 내용은 스레드 동기화(C# 프로그래밍 가이드)를 참조하십시오.
lock 키워드를 사용하면 다른 스레드가 코드의 임계 영역에 있는 동안에는 특정 스레드가 임계 영역에 들어갈 수 없습니다. 다른 스레드가 잠긴 코드에 들어가려고 할 경우 개체가 해제될 때까지 대기합니다.
스레딩에 대한 자세한 내용은 스레딩(C# 프로그래밍 가이드) 단원을 참조하십시오.
lock 키워드는 블록의 시작 부분에서 Enter를 호출하고 블록의 끝 부분에서 Exit를 호출합니다.
일반적으로 코드에서 제어되지 않는 인스턴스나 public 형식은 잠그지 않는 것이 좋습니다. 일반적인 구문 lock (this), lock (typeof (MyType)) 및 lock ("myLock")은 다음과 같이 이 지침을 위반합니다.
lock (this) - 해당 인스턴스에 공용으로 액세스할 수 있는 경우 문제가 됩니다.
lock (typeof (MyType)) - MyType에 공용으로 액세스할 수 있는 경우 문제가 됩니다.
lock(¡°myLock¡±) - 동일한 문자열을 사용하는 프로세스의 다른 코드가 동일한 잠금을 공유하게 되므로 문제가 됩니다.
가장 좋은 방법은 private 개체를 정의하여 잠그거나 private static 개체 변수를 정의하여 모든 인스턴스에 공통된 데이터를 보호하는 것입니다.
다음 샘플에서는 C#에서 잠금 없이 스레드를 사용하는 간단한 방법을 보여 줍니다.
//using System.Threading; class ThreadTest { public void RunMe() { Console.WriteLine("RunMe called"); } static void Main() { ThreadTest b = new ThreadTest(); Thread t = new Thread(b.RunMe); t.Start(); } } // Output: RunMe called
다음 샘플에서는 스레드와 lock을 사용합니다. lock 문이 있으면 문 블록이 임계 영역이 되고 balance는 음수가 되지 않습니다.
// using System.Threading; class Account { private Object thisLock = new Object(); int balance; Random r = new Random(); public Account(int initial) { balance = initial; } int Withdraw(int amount) { // This condition will never be true unless the lock statement // is commented out: if (balance < 0) { throw new Exception("Negative Balance"); } // Comment out the next line to see the effect of leaving out // the lock keyword: lock (thisLock) { if (balance >= amount) { Console.WriteLine("Balance before Withdrawal : " + balance); Console.WriteLine("Amount to Withdraw : -" + amount); balance = balance - amount; Console.WriteLine("Balance after Withdrawal : " + balance); return amount; } else { return 0; // transaction rejected } } } public void DoTransactions() { for (int i = 0; i < 100; i++) { Withdraw(r.Next(1, 100)); } } } class Test { static void Main() { Thread[] threads = new Thread[10]; Account acc = new Account(1000); for (int i = 0; i < 10; i++) { Thread t = new Thread(new ThreadStart(acc.DoTransactions)); threads[i] = t; } for (int i = 0; i < 10; i++) { threads[i].Start(); } } }
출처 : MSDN - http://msdn.microsoft.com/ko-kr/library/x13ttww7(v=VS.90).aspx
volatile 키워드는 동시에 실행 중인 여러 스레드에 의해 필드가 수정될 수 있음을 나타냅니다. volatile로 선언된 필드에는 단일 스레드를 통한 액세스를 전제로 하는 컴파일러 최적화가 적용되지 않습니다. 이렇게 하면 필드의 값을 항상 최신 상태로 유지할 수 있습니다.
일반적으로 volatile 한정자는 액세스를 serialize할 때 lock 문을 사용하지 않고 여러 스레드에서 액세스하는 필드에 사용됩니다. 다중 스레드 시나리오에서 volatile을 사용하는 방법의 예제는 방법: 스레드 만들기 및 종료(C# 프로그래밍 가이드)를 참조하십시오.
volatile 키워드는 다음과 같은 형식의 필드에 적용할 수 있습니다.
참조 형식
안전하지 않은 컨텍스트의 포인터 형식. 포인터 자체는 volatile일 수 있지만 포인터가 가리키는 개체는 volatile일 수 없습니다. 즉, "volatile 개체에 대한 포인터"를 선언할 수 없습니다.
sbyte, byte, short, ushort, int, uint, char, float 및 bool 같은 정수 계열 형식
정수 계열 형식을 기반으로 한 열거형
참조 형식으로 알려진 제네릭 형식 매개 변수
volatile 키워드는 클래스 또는 구조체의 필드에만 적용할 수 있습니다. 지역 변수는 volatile로 선언할 수 없습니다.
- 설정된 레벨이하가 로깅된다.
01.
public
class
SimpleLogTest {
02.
03.
private
final
Logger logger = Logger.getLogger(SimpleLogTest.
class
);
04.
05.
public
static
void
main(String[] args) {
06.
new
SimpleLogTest().test();
07.
}
08.
09.
private
void
test(){
10.
BasicConfigurator.configure();
11.
12.
log(Level.ALL);
13.
log(Level.TRACE );
14.
log(Level.DEBUG );
15.
log(Level.INFO);
16.
log(Level.WARN);
17.
log(Level.ERROR);
18.
log(Level.FATAL);
19.
log(Level.OFF);
20.
}
21.
22.
private
void
log(Level level){
23.
logger.setLevel(level);
24.
System.out.println(logger.getLevel());
25.
logger.trace(
"trace"
);
26.
logger.debug(
"debug"
);
27.
logger.info(
"info"
);
28.
logger.warn(
"warn"
);
29.
logger.error(
"error"
);
30.
logger.fatal(
"fatal"
);
31.
}
32.
}
0 [main] TRACE test.SimpleLogTest - trace
0 [main] DEBUG test.SimpleLogTest - debug
0 [main] INFO test.SimpleLogTest - info
0 [main] WARN test.SimpleLogTest - warn
0 [main] ERROR test.SimpleLogTest - error
0 [main] FATAL test.SimpleLogTest - fatal
TRACE
0 [main] TRACE test.SimpleLogTest - trace
0 [main] DEBUG test.SimpleLogTest - debug
0 [main] INFO test.SimpleLogTest - info
0 [main] WARN test.SimpleLogTest - warn
0 [main] ERROR test.SimpleLogTest - error
0 [main] FATAL test.SimpleLogTest - fatal
DEBUG
16 [main] DEBUG test.SimpleLogTest - debug
16 [main] INFO test.SimpleLogTest - info
16 [main] WARN test.SimpleLogTest - warn
16 [main] ERROR test.SimpleLogTest - error
16 [main] FATAL test.SimpleLogTest - fatal
INFO
16 [main] INFO test.SimpleLogTest - info
16 [main] WARN test.SimpleLogTest - warn
16 [main] ERROR test.SimpleLogTest - error
16 [main] FATAL test.SimpleLogTest - fatal
WARN
16 [main] WARN test.SimpleLogTest - warn
172 [main] ERROR test.SimpleLogTest - error
172 [main] FATAL test.SimpleLogTest - fatal
ERROR
172 [main] ERROR test.SimpleLogTest - error
172 [main] FATAL test.SimpleLogTest - fatal
FATAL
172 [main] FATAL test.SimpleLogTest - fatal
OFF
출처 : http://pantarei.tistory.com/783
싱글턴 패턴(Singleton Pattern) - for Beginner
이 문서는 GoF(Gang of Four) Design Patterns 에 정의된 패턴 목록 중 싱글턴 패턴(Singleton Pattern)을 다시 정리하면서 내용을 요약한 것이다. 개인적으로 자바와 닷넷 양진영에 모두 경험이 있다보니 동일 패턴에 대해서 상호 비교해보는 것이 어떨까 하는 생각이 들었다. 그래서 간략하지만 Java와 C# 양쪽에 걸쳐 내용을 작성하였으며, 소스코드 템플릿 또한 *.java, *.cs로 나누어 예를 제시하였다. 어쩌면 이 코드들 때문에 내용이 조금 더 복잡해 보일지도 모르겠다.
싱글턴 패턴의 개요
GoF의 23가지 디자인 패턴 중 개발자에게 가장 익숙한 패턴의 하나가 바로 '싱글턴 패턴(Singleton Pattern)'일 것이다. 싱글턴 패턴은 해당 클래스의 인스턴스(instance)가 하나만 만들어지고, 어디서든지 그 유일한 인스턴스에 접근할 수 있도록 하기 위한 패턴의로 정의된다.
GoF에 기술된 내용 중 싱글턴 패턴을 활용할 수 있는 상황은 다음과 같다.
- 클래스의 인스턴스가 오직 하나여야 함을 보장하고, 잘 정의된 접근 방식에 의해 모든 클라이언트가 접근할 수 있도록 해야 할 때.
- 유일하게 존재하는 인스턴스가 상속에 의해 확장되어야 할 때, 클라인트는 코드의 수정 없이 확장된 서브클래스의 인스턴스를 사용할 수 있어야 할 때.
이를테면 쓰레드 풀, 캐시, 대화상자, 사용자 설정이라든가 레지스트리 설정을 처리하는 객체, 로그 기록용 객체, 프린터나 그래픽 카드 같은 디바이스를 위한 디바이스 드라이버 같은 것들이 좋은 예가 될 것이다.
싱글턴의 기본적인 구조(Structure)는 그림과 같다.
[자바 코드 1]
// NOTE: This is not thread safe!
public class Singleton {
private static Singleton uniqueInstance;
// other useful instance variables here
private Singleton() {}
public static Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
// other useful methods here
}
아래는 동일한 형태의 C# 버전으로 된 코드이다.
[C# 코드 1]
// NOTE: This is not thread safe!
public sealed class Singleton
{
static Singleton instance=null;
Singleton()
{
}
public static Singleton Instance
{
get
{
if (instance==null)
{
instance = new Singleton();
}
return instance;
}
}
}
이 코드에서 Singleton 클래스는 private 변수와 생성자를 갖고 있으며 클라이언트에서 인스턴스를 요청할 때까지 Singleton 객체의 생성을 지연(lazy instantiation)하고 있다.
그런데 위의 코드 형태는 주석에도 달려있듯이 멀티(다중)쓰레딩 환경에서의 잠재적 문제를 안고 있기 때문에 실전에 절대 사용하면 안된다. 두개 이상의 쓰레드가 인스턴스를 획득하기 위해 getInstance() 메서드(C#의 경우 Instance 속성(Property))에 진입하여 경합을 벌이는 과정에서 서로 다른 두개의 Singleton 인스턴스가 만들어지는 좋지 않은 상황이 발생할 여지가 있다.
멀티쓰레드 환경에서의 싱글턴(Multithreaded Singleton)
위에서 제기한 문제를 해결하기 위해서는 다음 세가지의 해법을 사용할 수 있다.
- 인스턴스를 필요할 때 생성하지 않고, 처음부터 인스턴스를 만들어 버린다. 다시 말해서 lazy instantiation을 포기하고 static 멤버필드를 사용항여 언과 동시에 초기화하는 것이다. 단, 인스턴스를 미리 만들어 버리게 되면, 특히 해당 인스턴스가 자원을 많이 차지하는 컴포넌트일 경우에는 시스템 리소스가 쓸데없이 낭비될 가능성이 있다.
- getInstance() 메서드(C#의 경우 Instance 속성)를 동기화시킨다. 단, 동기화시키고자할 때는 getInstance()의 속도가 그렇게 중요하지 않다고 판단될 경우이며 동기화로 인한 오버헤드를 감수해야 한다. - 메서드를 동기화 시키면 일반적으로 성능이 100배 정도는 저하된다고 한다.
- DCL(Double-checked Locking) 기법을 사용한다. 단, 자바의 경우 DCL은 자바 5 버전 이상의 JVM 환경에서 인스턴스 변수에 volatile 키워드를 사용해야만 한다. voatile 키워드는 멀티쓰레드 환경에서도 uniqueInstance 변수가 원자성을 유지하도록 하여 올바른 싱글턴 인스턴스의 초기화가 진행되도록 한다(The volatile keyword in Java를 참고하라). 하지만 자바 1.4 및 그 이전에 나온 JVM에서는 메모리 모델의 문제로 제대로 동작하지 않는다는 것에 주의해야 한다(자세한 내용은 The "Double-Checked Locking is Broken" Declaration 참고하라).
설명보다는 코드를 보고 이해하는 것이 빠를 것 같다. 각 해법을 적용하여 멀티쓰레드 환경에서 제대로 동작(thread-safe)하는 싱글턴 구현의 예제 코드들이 아래에 있다.
1. 처음부터 인스턴스를 생성하는 예제 코드
[자바 코드 2]
public class Singleton {
private static Singleton uniqueInstance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return uniqueInstance;
}
}
[C# 코드 2]
public sealed class Singleton
{
static readonly Singleton uniqueInstance = new Singleton();
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static Singleton()
{
}
Singleton()
{
}
public static Singleton Instance
{
get
{
return uniqueInstance;
}
}
}
2. 동기화 예제 코드
[자바 코드 3]
public class Singleton {
private static Singleton uniqueInstance;
// other useful instance variables here
private Singleton() {}
public static synchronized Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
// other useful methods here
}
[C# 코드 3]
public sealed class Singleton
{
static Singleton uniqueInstance = null;
static readonly object padlock = new object();
Singleton()
{
}
public static Singleton Instance
{
get
{
lock (padlock)
{
if (uniqueInstance == null)
{
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
}
}
3. DCL(Double-checked Locking) 예제 코드
[자바 코드 4]
//
// Danger! This implementation of Singleton not
// guaranteed to work prior to Java 5
//
public class Singleton {
private volatile static Singleton uniqueInstance;
private Singleton() {}
public static Singleton getInstance() {
if (uniqueInstance == null) {
synchronized (Singleton.class) {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
[C# 코드 4]
public sealed class Singleton
{
static Singleton uniqueInstance = null;
static readonly object padlock = new object();
Singleton()
{
}
public static Singleton Instance
{
get
{
if (uniqueInstance == null)
{
lock (padlock)
{
if (uniqueInstance == null)
{
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
}
아래 C# 코드는 위 코드와 동일하게 DCL을 사용하지만 volatile을 사용하는 다른 버전의 예제이다.
[C# 코드 5]
public class Singleton
{
private static volatile Singleton uniqueInstance = null;
protected Singleton()
{
}
public static Singleton Instance()
{
if (uniqueInstance == null)
{
lock (typeof(Singleton))
{
if (uniqueInstance == null)
{
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
싱글턴 레지스트리(Singleton Registry)
서두에서 "유일하게 존재하는 인스턴스가 상속에 의해 확장되어야 할 때, 클라인트는 코드의 수정 없이 확장된 서브클래스의 인스턴스를 사용할 수 있어야 할 때" 싱글턴을 활용한다고 하였다. 이때에는 서브클래스를 만드는 것이 중요한 게 아니라, 이 새로운 서브클래스의 유일한 인스턴스를 만들어 클라이언트가 이를 사용할 수 있도록 하는 것이 관건이다.
싱글턴의 서브클래스를 만들 때 가장 유연한 방법은 싱글턴에 대한 레지스트리를 사용하는 것이다. 아래 자바 예제 코드는 레지스트리를 갖고 있는 싱글턴으로 특정 클래스 객체의 인스턴스를 생성하기 위해서 리플렉션을 사용하고 있다. 'classname'은 Singleton의 서브클래스 이름이다. 이렇게 하면 서브클래스의 선택에 있어서 런타임에 싱글톤을 결정하는 유연성을 가질 수 있다(자세한 내용은 Simply Singleton을 참고하라).
[자바 코드 5]
import java.util.HashMap;
import org.apache.log4j.Logger;
public class Singleton {
private static HashMap map = new HashMap();
private static Logger logger = Logger.getRootLogger();
protected Singleton() {
// Exists only to thwart instantiation
}
public static synchronized Singleton getInstance(String classname) {
Singleton singleton = (Singleton) map.get(classname);
if (singleton != null) {
logger.info("got singleton from map: " + singleton);
return singleton;
}
try {
singleton = (Singleton) Class.forName(classname).newInstance();
}
catch(ClassNotFoundException cnf) {
logger.fatal("Couldn't find class " + classname);
}
catch(InstantiationException ie) {
logger.fatal("Couldn't instantiate an object of type " + classname);
}
catch(IllegalAccessException ia) {
logger.fatal("Couldn't access class " + classname);
}
map.put(classname, singleton);
logger.info("created singleton: " + singleton);
return singleton;
}
}
결론
이상으로 멀티쓰레딩 환경에서의 싱글턴 패턴 구현 코드를 들여다 보았다. 그렇다면 이 세가지 중 어떤 코드 템플릿을 사용하는 것이 좋을까?
자바에서는 Double-checked locking과 Singleton 패턴 등 (조금 오래되긴 했지만) DCL과 관련한 문서들을 참고해보면 멀티쓰레드 환경에서 제대로 동작하는 싱글턴을 만들기 위한 최상의 솔루션은 동기화를 수락하거나 static 멤버필드를 사용하는 것을 권장하고 있다. 닷넷의 경우 The Correct Double Checked-Lock Pattern Implementation를 보면 [C# 코드 2]와 같은 형태의 코드를 사용할 것을 권장하고 있다.
싱글턴 구현에 있어서 반드시 DCL을 사용해야 하는 특별한 경우가 아니라면 대부분의 상황에서는 static 변수를 사용하거나 동기화 블럭을 사용하는 것으로도 충분할 것 같다. 성능의 저하는 다소 존재하겠지만 다양한 java 및 .net 버전과 메모리 모델에 종속적이지 않은 싱글턴을 구현하는 잇점도 있다고 생각한다. DCL을 적용해야한다면 특히 자바의 경우 volatile 키워드와 함께 반드시 자바 5 버전 이상을 사용해야 한다는 것을 잊지 말아야 한다.
마지막으로 참고가 될만한 두가지 사항을 덧붙이며 싱글턴 패턴에 대한 요약을 마무리한다.
싱글턴 패턴 사용 시 주의할 점(Java 기준)
- 중복되는 얘기지만 DCL을 사용하려면 자바 5(1.5) 이후 버전을 사용해야 한다.
- 클래스 로더가 여러개 있으면 싱글턴이 제대로 작동하지 않고, 여러 개의 인스턴스가 생길 수 있다. 이 경우 클래스 로더를 직접 지정해서 사용해야한다.
- 개인적으로 최근 프로젝트 환경을 보면 슬슬 자바 5 버전으로 많이 갈아타고 있는 듯 하다. 정말 오래된 시스템을 유지 보수하는 경우가 아니라면 자바 1.2 이전 버전을 사용할 일은 없겠지만, 혹시라도 자바 1.2 이전 버전의 환경에서 작업한다면 JVM의 가비지 컬렉터 관련 버그 때문에 싱글턴 레지스트리를 사용해야할 수도 있다.
아래 코드는 클래스 로더를 직접 지정하는 예제이다. 이 코드는 Class.forName() 메서드를 대체할 수 있다(자세한 내용은Simply Singleton을 참고하라).
[자바 코드 6]
private static Class getClass(String classname) throws ClassNotFoundException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader == null) {
classLoader = Singleton.class.getClassLoader();
}
return (classLoader.loadClass(classname));
}
정적 클래스 변수(메서드) vs. 싱글턴 패턴
굳이 싱글턴 패턴을 사용할 필요없이 전역 클래스 변수(static 멤버필드)를 사용하면 되지 않을까 하는 의문이 들 수도 있다. lazy instantiation을 구현하는 싱글턴 패턴에 비해서 전역 변수를 사용하는 경우 다음과 같은 단점들이 있을 수 있다.
- 싱글턴 패턴은 static 인스턴스를 미리 생성해놓는 경우를 제외하고는 객체가 필요한 상황이 되었을 때에 비로소 인스턴스를 생성한다. 반면 전역 변수를 사용하면 대부분의 경우는 어플리케이션을 시작할 때 미리 객체가 생성한다. 그런데 그 객체가 자원을 많이 차지하고, 실제로 어플리케이션을 종료할 때까지 한번도 쓰지 않게된다면 괜한 자원만 낭비하는 꼴이 되고만다(이러한 상황은 시스템 플랫폼에 따라 달라질 수도 있다. 어떤 JVM은 객체를 나중에 필요할 때 생성하기도 한다고 한다).
- 전역 변수를 사용하다 보면 간단한 객체에 대한 전역 레퍼런스를 자꾸 만들게 되면서 네임스페이스를 지저분하게 만드는 경향이 생긴다. 물론 싱글턴도 남용될 수 있지만, 네임스페이스가 지저분해지게 되는 것을 부추길 정도는 아니다.
참고 자료
아래는 이 포스트를 작성하기 위해 참고한 도서와 관련 사이트의 목록이다. 영어가 짧고 스크롤의 압박이 심하다보니 사이트의 글들을 죄다 꼼꼼하게 읽어보지는 못했다. ^^; 하지만 정독해보면 분명 도움이 될 내용들이라고 장담한다. ^^
Books
- GOF의 디자인 패턴, 피어슨에듀케이션 코리아
- Head First Design Patterns, 한빛미디어
- 예제로 배우는 C# 디자인 패턴, 정보문화사 (비추. 자바와 닷넷 관련한 더 좋은 패턴 책들이 많이 있다.)
- J2EE 패턴 (GoF & J2EE), SUN SL-500
- The Design Patterns Java Companion
Terms
Articles
- Double-checked locking과 Singleton 패턴
- 디자인 패턴으로 알아본 Double Checked Lock(DCL)
- The "Double-Checked Locking is Broken" Declaration
- The Correct Double Checked-Lock Pattern Implementation
- Simply Singleton
- Exploring the Singleton Design Pattern
- Implementing Singleton in C#
- Performance of techniques for correctly implementing lazy initialization
출처 : http://kyungseo.pe.kr/blog/111
출처 : http://perfectchoi.blogspot.com/2009/11/log-class-%EC%84%A4%EA%B3%84.html
Log Class 설계
현재 상태를 사용자에게 정확하고 자세하게 알려줄 수 있는 유일한 도구이다.
로그라는 특성상 어느 곳에서든지 사용하기 편하도록 Singleton클래스를 상속받아 전역 단일체 클래스로 만들자
로그를 필요한 때에 남기지 못하고 또 정확하게 남기지 못하면 서버가 제대로 동작하고 있는지, 문제는 없는지에 대한 정보를 정확하게 파악할 수 없다.
체계 없이 남겨진 로그 정보는 실제 필요한 로그를 찾는데 시간이 오래 걸 릴 뿐만 아니라 그게 대한 정보가 부족해 결국 시스템 부하만 줄 뿐 기능상으로는 유명무실해 진다.
1. Log 클래스의 종류를 나누자
- 알림과 오류로 나눠 자신이 원하는 정보를 보다 빨리 찾을 수 있도록 설계 하고
2. Log 클래스의 중요도를 나누자
- 각 종류마다 중요도 LOW, NORMAL, HIGH, CRITICAL등 등급을 나누어 쉽게 정보의 중요성을 파악할 수 있게 하자
2-1. 아래 나와 있는 enumLogInfoType은 Log클래스에서 사용할 로그 정보의 분류를 나열한 것이다
enum enumLogInfoType
{
LOG_NONE= 0x00000000,
LOG_INFO_LOW= 0x00000001,
LOG_INFO_NORMAL= 0x00000002,
LOG_INFO_HIGH= 0x00000004,
LOG_INFO_CRITICAL= 0x00000008,
LOG_INFO_ALL= 0x0000000F,
LOG_ERROR_LOW= 0x00000010,
LOG_ERROR_NORMAL= 0x00000020,
LOG_ERROR_HIGH= 0x00000040,
LOG_ERROR_CRITICAL= 0x00000080,
LOG_ERROR_ALL= 0x00000100,
LOG_ALL= 0x000001FF
};
각 등급마다 16진수로 설정하고 OR 연산을 할 수 있도록 2의n승으로 증가시킨다.
- 로그를 저장할 매체 : 파일, 출력 창, 윈도우, DB, TCP, UDP로 분류
enum enumLogStorageType
{
STORAGE_F ILE= 0x00000000,
STORAGE_DB= 0x00000001,
STORAGE_WINDOW= 0x00000002,
STORAGE_OUTPUTWND= 0x00000003,
STORAGE_UDP= 0x00000004,
STORAGE_TCP= 0x00000005
};
위의 enumLogStorageType은 로그를 저장할 매체에 대한 선언이다.
로그의 부하로 인해 속도에 문제가 생길 수 있기 때문에 최소한의 처리(로그를 내부 Queue에 넣고 바로 반환)만 하고 내부적으로 틱 쓰레드를 사용하여 일정 시간마다 Queue에 들어있던 로그를 가져와 처리하도록 한다.
서비스 할 때에는 로그를 최소한으로 하여 사용해야 한다.
3.로그를 남기는 시점
3-1. 에러 로그의 경우 모든 경우에 로그로 남긴다.
- 쉽게 넘긴 작은 에러 하나가 정말 큰 버그를 부른다.
3-2. 일반적인 정보 로그의 경우
- 함수 시작 부분과 모든 패킷 처리 시작 부분에 남기는 것이 좋다.
- 이유는 만약 어떤 처리를 하다 이유 없이 서버 프로세스가 죽었을 경우에 쉽게 그 위치를 파악할 수 있다.
- 모든 함수와 패킷에서 로그를 남긴다면 실제 서비스할 때에는 엄청난 부하가 생길 것이다. 그래서 앞에서 정한 등급에 따라 일정 등급 이하의 정보를 로그로 남기지 않거나 정보 관련 로그는 아예 남기지 않도록 할 수 있다.
4.무엇을 로그로 남길 것인가
4-1. 로그가 어떤 함수에서 남겨졌는지 알아야 한다.
4-2. 누구에 의해서 로그가 남겨졌는지를 알아야 한다.
- 시스템일수도 있고 패킷을 받아 남기는 것이라면 패킷을 보낸 해당 클라이언트가 될 수도 있다.
4-3. 로그가 발생되는 시간을 남겨야한다.
4-4. 로그의 종류가 에러라고 하면 에러가 어떤 작업을 하다가 발생한 것인지 남겨야하고 종류가 알림이라면 함수의 인자나 받은 패킷의 프로토콜 내용을 남겨야 한다.
작성자:HwansChoi 하얀가지 시간: 오후 4:24
출처 : http://loveev.tistory.com/15
.NET Application 을 이용해서 새로운 프로젝트를 만들때마다 프로그램 로그를
기록할 괜찮은 Library가 없어 매번 방황하거나 간단하게 Trace나 FileInfo로 열어서 쌓는 수준으로만 해왔다.
좀 찾아보다가 Log4Net 이란 것을 발견했다.(지인의 소개로 발견 ^^;;)
간단한 형식으로 빠르게 FileInfo 를 열어서 파일에 기록하면 시간은 단축할 수 있다.
그러나 매번 잊어먹는 Syntax찾아가며 새로 작성하기란 여간 힘든 일이 아니다.
더군다나 멀티 스레드 동기화 까지 하라고 하면 하다가 GG 치는 경우가 많다.
이 Log4Net은 파일로도 쓰고
Database에도 쌓고
여타 다른 매체에도 손쉽게 설정만 해주면 사용가능한 유용한 Library이다.
물론 Thread Safe 하게 작성되어졌으리라 믿어 의심치 않는다.(믿는 도끼에 발등...)
Log4Net 의 간단한 History 나 Log4J 에 관한 상세 내용은 링크 사이트를 참조하길 바란다.
간단하게 파일과 MS SQL Server 에 Log를 쌓는 예제를 .NET C#을 이용해 작성해보았다.
1. 예제환경
- OS: Microsoft Windows XP Professional Version 2002 Service Pack 2( 길다..-_-;)
- IDE: Microsoft Visual Studio 2005 Professional Edition
- Language: C#
- .NET Framework v2.0.50727
- MS-SQL 2000
2. Log4Net Download
- 여기에서 다운로드 받은 다음에 압축을 푼다.
3. Log4Net 을 이용한 간단한 로그 쓰기 예제(Console, 파일, Database)
3.1 Project 생성 ( C# 프로젝트 간단하게 Console 이나 Windows Application 으로 생성)
3.2 Add Reference
- download 받은 Log4Net 안에서 log4net-x.x.xx\bin\net\ 에 들어가서 적당한 버전
선택 후 log4net.dll, log4net.pdb 를 복사한 다음 프로젝트 Root에 넣고 IDE 에서
Add Reference 를 한다.
(Add Reference를 할때 Browse Tab 에서 Dll을 직접 선택하면 된다.)
3.3 Log4Net configuration File
- 설정파일(.xml)은 프로젝트에 Root에 넣어두면 Build 될때 Bin\Debug or Release
밑으로 자동 복사된다.
- 공용으로 쓰기위해 어디에다가 위치시켜도 좋지만 편의를 위해서 .exe 가
Project Root에 두면 상대경로 지정이 가능하기에 편의상 프로젝트 Root에 두었다.
- 첨부된 예제의 설정파일에 보면 <appender>...</appender> 가 3개 존재 한다.
- 각각이 차례대로 Console, File, Database를 쓰는 예제이다.
- 주의 사항
1. 설정파일의 맨 마지막 하단에 작성한 appender-ref 를 추가하는 것을 잊으면
안된다. 빠뜨려서 동작하지 않는 경우가 발생한다.
2. Database 부분의 Appender가 있는데 bufferSize를 통상 100으로 된 곳도 있는데
그렇게 하면 100라인이 될때까지 로그를 쓰지 않아 동작하지 않는다고 오해한다.
1로 고치면 해결된다.
3. 현재 자신의 .Net Framework 버전이 1.1 인지 2.0인지에 따라서 Database
appender에 <connectionType> 에 value 정보의 Version 정보를 정확히
기재해주어야한다. 그렇지 않으면 이 역시 오동작을 하게 될 것이다.
SqlConnection 의 버전 정보를 읽어오는 방법은 다음과 같이 잠시 써서
AssemblyName을 얻으면 Version 정보도 포함되어 있으니 확인가능하다.
----- AssemblyName 버전 확인 방법 ----
SqlConnection sq = new SqlConnection();
sq.ConnectionString = "";
Assembly asm = Assembly.GetAssembly(sq.GetType());
AssemblyName asmName = asm.GetName();
4. Database에 Table은 Log 라는 Table 명으로 미리 만들어 둔다.
CREATE TABLE [Log2] (
[Id] [int] IDENTITY (1, 1) NOT NULL ,
[Date] [datetime] NOT NULL ,
[Thread] [varchar] (255) NOT NULL ,
[Level] [varchar] (50) NOT NULL ,
[Logger] [varchar] (255) NOT NULL ,
[Message] [varchar] (4000) NOT NULL ,
[Exception] [varchar] (2000) NULL
) ON [PRIMARY]
GO
5. Log Level 은 appender-ref 가 있는 <root> 부분에 있으니 설정하면 설정한
로그레벨 이상만 출력이 될것이다.
DEBUG < INFORMATION < WARNING < ERROR < FATAL 순서이다.

예제로 작성된 Source 파일
< 예제로 작성된 설정파일을 참고 하자 >
손쉬운 프로그램 로그 Library로 Log Library 작성하는 삽질은 피해보자.
출처 : http://blog.naver.com/PostView.nhn?blogId=mrlongpark&logNo=150055610472
■log4net 내려받기
http://logging.apache.org/log4net/download.html
incubating-log4net-1.2.10.zip 을 다운로드받는다.
다운로드받은 파일의 압축을 푼다.
..\log4net-1.2.10\bin\mono\2.0\release 폴더의 log4net.dll과 log4net.xml 파일을 사용한다.
■환경설정
VS 솔루션의 참조추가를 선택해서, log4net.dll을 추가한다.
AssemblyInfo.cs 에 아래내용을 추가한다.
[assembly: log4net.Config.DOMConfigurator(Watch = true)]
■사용방법(프로그램부)
Application의 기본적인 구성은 아래와 같다.
1.(프로그램)logger로 불려지는 log출력용 인스턴스를 취득한다.
2.(프로그램)logger의 메서드를 사용해서 로그를 설정한다.
3.(XML파일)출력에 사용하는 Appender를 설정한다.
4. (XML파일)출력하고싶은 logger단계와 Appender를 매핑한다.
출력하는 메시지는 프로그램에 기술하고,출력에 관한설정은 구성파일 App.config에 기술한다.
■로그출력용 인스턴스(logger)의 취득
출력하고 싶은 Class에 아래와 같이 선언한다.
private static readonly log4net.ILog logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
■로그에 출력하는 메서드
logger.Fatal("log4net에의한출력예~Fatal(치명적장해)");
logger.Error("log4net에의한출력예~Error(장해)");
logger.Warn("log4net에의한출력예~Warn(경고");
logger.Info("log4net에의한출력예~Info(정보)");
logger.Debug("log4net에의한출력예~Degug(디버그트레이스용)");
레벌 | 표준적인 사용분류 |
Fatal | 시스템 정지에 해당하는 치명적 장해 |
Error | 시스템이 정지하지는 않지만, 문제가 되는 장해 |
Warn | 장해가 아닌 주의경고 |
Info | 동작로그등의 정보 |
Debug | 개발용 디버그 메시지 |
■로그출력할 곳 [어펜더(Appender)]의 설정
<appender name="FileAppender" type="log4net.Appender.FileAppender">
<param name="File" value="d:\myDev\log\log.log" />
<param name="AppendToFile" value="true" />
</appender>
■대표적인 [어펜더(Appender)]
ADONetAppender
데이터베이스의 보존하기위해, 복수의 AP서버가 있는 경우에는 일관관리가 가능한 메리트가 있다.
그러나,DB접속이 안될때는 SMTPAppender와 함께써서 장해통보를 할 필요가 있다.
ConsoleAppender
콘솔에 출력을 하는 표준 Appender이다.
EventLogAppender
이벤트뷰어라고 하는 Windows에 속해있는 이벤트로그 감시화면에 application로그를 출력한다.
FileAppender
파일에 로그를 출력한다. 간단하지만 파일크기가 너무 크게 될 가능성이 있기 때문에RollingFileAppender를 사용하는 것을 추천한다.
NetSendAppender
Messenger서비스에 동작하는 NetSend명령을 사용해서, 장해를 직접적으로 사용자에게 통지하는 것이 가능하다.
RollingFileAppender
파일의 로그를 출력한다. 파일크기와 시각에 의해 자동적으로 로그파일을 분활하는 것이 가능해, 이용이 많은 Appender이다.
SMTPAppender
메일송신을 한다. TO에 복수지정이 되지만,CC와 BCC의 지정이 되지않는다.
UdpAppender
UDP(user Datagram Protocol)에 로그를 출력한다. UDP 수신처는 log4j의 로그뷰어인Chainsaw 등을 지정할 수 있다.
ASPNetTraceAppender
ASP에서 사용 가능한 Appender이다.
■Layout 설정
Log4net에서는 프로그램에서 기술한 메시지뿐만 아니라, 일시,레벨 등의 부가적인 정보도 합쳐서 출력할 수 있다.
<appender name="FileAppender" type="log4net.Appender.FileAppender">
<param name="File" value="d:\myDev\log\log.log" />
<param name="AppendToFile" value="true" />
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%d [%t] %-5p %c [%x] - %m%n" />
</layout>
</appender>
■PatternLayout으로 지정가능한 패턴
패턴 | 설명 |
%c | 로그출력이 된 logger명을 출력 |
%C | Class를 출력 |
%d | 일시를 출력. 「%d{yyyy/mm/dd HH:mm:ss}」와 같은 상세설정도 가능 |
%F | 파일명을 출력 |
%l | 소스명이나행과 같은 호출한 위치를 출력 |
%L | 행번호를 출력 |
%m | 메시지를 출력 |
%M | 메서드명을 출력 |
%n | 개행문자를 출력 |
%p | 로그의 레벨(Fatal/Error등)을 출력 |
%t | 로그를 생성한 쓰레드를 출력 |
%x | 쓰레드의 NDC(네스트화 진단 컨텍스트)를 출력.쓰레드고유의 정보(session ID등)을 밀어넣는것이 가능 |
단, %C、%F、%l、%L、%M 의 경우는 처리부하가 높아지기 때문에 필요할 때 이외는 사용하지 않도록 주의가 필요.
또한, Layout에는 SimpleLayout,XmlLayout등의 몇 개의 종류가 있기는 하지만, 대부분의 경우,로그 정리가 간단한 PatternLayout을 사용한다.
■출력대상 logger를 설정
Log4net에서는 logger의 계층으로 로그출력을 제어하는 것이 가능하다.
Log4netSample
├ MyTeam
│ └ MyProgram
└ OtherTeam
<!—담당팀의 로그는 WARN이상을 출력. 파일에도 출력 -->
<logger name="Log4netSample.MyTeam">
<level value="WARN" />
<appender-ref ref="FileAppender" />
</logger>
<!—다른팀의 로그는 FATAL이상을 출력 -->
<logger name="Log4netSample.OtherTeam">
<level value="FATAL" />
</logger>
<!—담당프로그램의 로그는 DEBUG이상을 출력 -->
<logger name="Log4netSample.MyTeam.MyProgram">
<level value="DEBUG" />
</logger>
<logger>태그를 이용해서 logger의 계층별로 레벨과 Appender를 설정하는 것이 가능하다.
레벨설정은 <level>태그를 설정해서,appender설정은 <appender-ref>태그를 사용할 수 있다.
appender는 복수설정할 수 있어서, 하나의 로그메세지를 복수의 출력처에 송신하는 것도 가능하다.
■Root의 설정
전체의 default가 되는 설정을 한다.
<root>
<level value="ERROR" />
<appender-ref ref="ConsoleAppender" />
</root>
로그출력의 default설정은 Root(logger계층의 최상위를 의미)라고 하고, <root>태그로 지정한다.
■정리
1. 개발에서 운영까지 일관되게 사용가능
2. 로그의 출력처가 풍부(파일,메일,DB등)
3. 동작중의 Application의 로그취득내용을 설정파일의 변경으로 동적으로 변경가능
4. Open소스(Apache Logging Service)
[출처] [winForm]log4net 설정하는 방법|작성자 롱박
출처 : http://nohungry.net/tt1/162
부모 폼과 자식 폼이 있다; 자식 폼에서 A라는 버튼을 클릭했을 때; B라는 텍스트 박스의 내용을 부모 폼의 C라는 텍스트 박스에 표시하고 싶다; 그 방법은?
물론; 가장 간단한 방법은 private으로 선언되어 있는 C라는 텍트스 박스를 public으로 선언하고; 자식 폼에서 직접 이 텍스트 박스에 접근하여; 값을 대입하는 방법이 있습니다;
하지만; 개인적으로 이 방법은 C#이 지향하는 OOP(Object-oriented paradigm)를 위배하고; 깔끔하지 못한 방법으로 별로 권장하고 싶지 않네요;
개인적으로 권장하는 것은 이벤트(event)를 이용하는 것 입니다; 이벤트는 단순히 폼 간의 어떤 데이터를 주고 받을 수 있을 뿐만 아니라; 서로 다른 클래스 간의 데이터 전송에도 유용하며, 데이터를 구조체 형식으로도 넘길 수 있는 등 다양한 장점이 있지요; 무엇보다, OOP를 준수하면서도; 깔끔한 코드를 유지할 수 있다는 것에 후한 점수를 주고 싶군요;
이 이벤트를 구현하는 방법에 가장 핵심이 되는 내용은 바로 delegate 입니다.
C++/MFC, java 등 다른 언어에 능숙한 사람들 대부분이 C#에 금방 적응하게 됨에도 불구하고; 그나마 C#에서 가장 당황하는 개념이 delegate가 아닐까 싶네요; (나만 그런가?;)
delegate는 한글로 굳이 번역하자면; 대리자라고 하는데; C#이 다른 언어와 차별성을 가지는 중요한 몇 몇 이슈 중 하나가 이 녀석의 존재라고 생각합니다;
단어 뜻으로만 이해하자면; 어려우니까; 서두에 언급한 자식 폼에서 부모 폼으로 텍스트를 전달하는 예제를 통해; delegate의 역할을 이해해보죠;
이미 언급했지만; 자식 폼에서 부모 폼으로 데이터를 전달할 때, 이벤트를 이용하기로 하였는데; 이벤트에서 가장 기본적인 사실은; 이벤트를 발생하는 곳이 있으면, 이벤트를 받는 쪽도 있어야 된다는 것입니다;
따라서, 자식 폼에는 이벤트를 발생시키는 녀석을 정의하고, 부모 폼에서는 이벤트를 받는 녀석을 정의해야 된겠지요. 이미 C#을 이용해 윈 폼 프로그래밍을 해본 사람이라면; 폼에 버튼을 붙이고; 클릭 이벤트를 추가해서; 버튼을 클릭하면; 메시지 박스로 "Hello, World!" 정도는 경험이 있을 것 입니다;
이 때, 버튼도 우리가 예로 들려는 자식 폼과 같습니다; 버튼이 이벤트를 발생시키고, 부모 폼에서 버튼 이벤트를 받아, 거기서 메시지 박스를 띄운 것이기 때문이지요;
자 이벤트를 정의하는 과정은 다음과 같습니다;

클래스 ChildFormEventArgs는 이벤트 데이터가 들어 있는 클래스에 대한 기본 클래스인 EventArgs를 상속 받아 구현하였습니다. 우리는 자식 폼에서 부모 폼으로 텍스트를 전송할 목적이므로, 기본 클래스에 message란 속성을 추가하였구요;
그리고 그 아래 이벤트 대리자(delegate)를 선언하는데, 대리자를 통해 넘기는 파라미터에 주목해봅시다;
ChildFormEventArgs는 앞에 언급한대로 이벤트 데이터 이고, object sender는 뭘까요?
이벤트를 발생 시킨 주체입니다; 우리의 예에서는 바로 자식폼이 되겠지요;
이벤트를 위한 대리자를 선언했으면; 자식 폼에 이 이벤트를 발생시키기 위한 코드를 구현해야 합니다;

이벤트 선언부를 눈여겨 보세요;
이에 앞서 선언한 delegate에 이벤트를 위한 엑세스 한정자인 event를 붙였습니다; 그리고 파생 클래스에서 이 이벤트를 일으키거나 override 할 수 있도록 protected와 virtual 키워드를 붙였지요;
그리고 그 아래 정의된 메소드는 자식 폼에 추가한 버튼을 클릭하면 호출되는 녀석; 이미 알고 있듯이; 우리는 버튼을 클릭했을 때, 자식 폼에 있는 텍스트 박스(txtSentMessage)에 적힌 텍스트를 부모 폼에 전달하기 위함이므로; 앞서 정의한 event를 발생시킬 수 있는 NotifyEvent를 호출하도록 하였습니다;
그럼, 이제 부모 폼에서 이벤트를 받을 수 있도록 구현해보죠;

부모 폼의 생성자(constructor)에 자식폼의 메모리를 할당하고; 이벤트를 받기 위해 OnNotifyParent이벤트 핸들러를 추가합니다;
자, 여기서 대리자(delegate)의 역할을 이해할 수 있습니다. 자식 폼에서는 결코 부모 폼의 child_OnNotifyParent 메소드를 직접적으로 호출하지 않습니다. 단지, 이벤트로 정의한 OnNotifyParent(delegate)를 구현하였고; 이를 이용해 이벤트를 발생시키는 NotifyEvent 메소드만 구현했을 뿐이지요; 하지만 OnNotifyParent는 대리자란 명칭에 걸맞게; NotifyEvent가 호출되면; 간접적으로 child_OnNotifyParent 해줬습니다;
이 때 이벤트 데이터를 전달해주는 ChildFormEventArgs를 통해 자식폼의 텍스트 박스에 적힌 텍스트를 가져올 수 있네요;