'Software Architect/C#'에 해당되는 글 30건

Software Architect/C#

http://preludeb.egloos.com/4097804

저작자 표시 비영리 변경 금지
신고
Software Architect/C#


원문 : http://blog.lovecoco.net/94

 

 

DirectX Software Develpment Kit

http://www.microsoft.com/downloads/details.aspx?FamilyID=86cf7fa2-e953-475c-abde-f016e4f7b61a&displaylang=en&Hash=qeJBP6Uksx0zY%2btLhcKYj%2fx4PDbDDOI%2btC%2bsa73SX4pkMysJCEnMU48j49VRn6%2fbDsK5k8OfHBLz%2fe9FNHN8hg%3d%3d

위 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를 누른다.

저작자 표시 비영리 변경 금지
신고
2 0
Software Architect/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#)

By 11 Mar 2013
 
  • Download Contents: Precompiled DLL, Source Code and Demo App, ThemeBuilder 
  • Supports Visual Studio 2008, 2010, 2012 
  • Supports .NET Framework 2.0, 3.5 4.0, 4.5 
  • Released on 24 Feb 2013  

 Note: Releases (13Jan2013) and newer are containing a ThemeBuilder which enables Ribbon to load/change Theme Easily. See ThemeBuilderForm inside the Demo.  

Content


Part 1: Background 

The ribbon that is going to be used in this article is an open source project created by Jose Menendez Poo. However, the original author of the ribbon has stopped support of it. A group of fans of this ribbon re-host and continue to develop/enhance and support the ribbon. 

The original ribbon creator has posted an article explaining what this ribbon is all about at here: [A Professional Ribbon You Will Use (Now with orb!)]. However, that article doesn't describe how to use it in your project. Therefore, this article will show how to use it.

Old Site: http://ribbon.codeplex.com (By original author, but has stopped support) 

New Site: http://officeribbon.codeplex.com (Re-host by fans of the ribbon) 

All releases starting (10 Jan 2013) are supporting

  • Visual Studio 2008, 2010 and 2012. 
  • .NET Framework 2.0, 3.5, 4.0 and 4.5 


Part 2: How to Use this Ribbon Control    

Reminder: Please note that this ribbon does not work on .Net 3.5 Client Profile and .NET 4.0 Client Profile. You have to switch the target framework to .NET 3.5 or .NET 4.0. When you first create a project, Visual Studio might initially set the target framework to Client Profile

1. Get System.Windows.Forms.Ribbon35.dll from download.

2. Create a blank WinForms project.


3. Add Ribbon into Visual Studio Toolbox.

Right Click on Toolbox > Add Tab.

Give the new tab a name "Ribbon".

Right Click on the New Tab [Ribbon] > Choose Items...

[Browse...] Where are you? System.Windows.Forms.Ribbon35.dl?

There you are... Gotcha... Select it...

Only [Ribbon] can be dragged into Form. Others, as the picture below said, they are not needed to exist in toolbox. However, its not going to harm your computer or project if you select all the items belongs to ribbon (by default). Its up to you.

And finally, what you're going to do is just...

Another Way

Manually code it behind.

You can add the ribbon into WinForm too with code behind.

Add a reference of  System.Windows.Forms.Ribbon35.dll into your project. Build the the solution.  

Open the designer of Main Form. In this example, Form1.Designer.cs.

Add these three lines of code 

private System.Windows.Forms.Ribbon ribbon1;
ribbon1 = new System.Windows.Forms.Ribbon();
this.Controls.Add(ribbon1); 

into Form1.Designer.cs 

private void InitializeComponent()
{
    ribbon1 = new System.Windows.Forms.Ribbon();
    this.components = new System.ComponentModel.Container();
    this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
    this.Text = "Form1";
    this.Controls.Add(ribbon1);
}
private System.Windows.Forms.Ribbon ribbon1; 

Save and Close Form1.Designer.cs.  

Double click and open Form1.cs, and now the Ribbon control is added into the main form.

 

Lets continue... 

4. Click on the Ribbon and click Add Tab.  

5. Click on the newly added RibbonTab, then click Add Panel.

6. Click on the newly added RibbonPanel, go to Properties. You will see a set of available controls that can be added to the RibbonPanel.

You might not able to see the extra command links of "Add Button", "Add ButtonList", "Add ItemGroup"... etc at the Properties Explorer.  

 

Right click at the Properties Explorer and Tick/Check the [Commands].  

 

 

7. Try to add some buttons into the RibbonPanel.

8. Click on the RibbonButton, go to Properties

9. Let's try to change the image and the label text of the button.

10. This is how your ribbon looks like now.

11. Now, create the click event for the buttons. Click on RibbonButton, go to Properties, modify the Name of the button.

12. Click on the RibbonButton, go to properties > Click on Events > Double Click on event of Click

 

13. Events created.

public Form1()
{
    InitializeComponent();
}

void cmdNew_Click(object sender, EventArgs e)
{
    MessageBox.Show("Button \"New\" Clicked.");
}

void cmdSave_Click(object sender, EventArgs e)
{
    MessageBox.Show("Button \"Save\" Clicked.");
}

14. Press F5 to run the application. Done.

15. You might want to inherit your Main Form into a RibbonForm to have extra features. Such as:

Note: Inherit the Main Form to RibbonForm will have some compatibility problems with some of theSystem.Windows.Forms controls. (especially MDI Client Control) 

16. In the code for Form1.cs, change inheritance of Form this line:

public partial class Form1 : Form

to RibbonForm 

public partial class Form1 : RibbonForm


Part 3: Caution While Using With Visual Studio 2010 

... deleted .... 


Part 4: Using this Ribbon with an MDI Enabled WinForm 

The following guide will show how to apply this ribbon with an MDI (Multi Document Interface) enabled WinForm. 

Start

  1. Let's first create a Ribbon application with the edited System.Windows.Forms.Ribbon.dll like this. Don't inherit the MainForm (the form that contains the ribbon control) with RibbonForm. Inheritance ofRibbonForm is not compatible with the MDI client control.
  2. Create the Click event for the ribbon buttons.
  3. public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
    
            cmdCloseForm.Click += new EventHandler(cmdCloseForm_Click);
            cmdForm1.Click += new EventHandler(cmdForm1_Click);
            cmdForm2.Click += new EventHandler(cmdForm2_Click);
            cmdWelcome.Click += new EventHandler(cmdWelcome_Click);
        }
    
        void cmdWelcome_Click(object sender, EventArgs e)
        {
    
        }
    
        void cmdForm2_Click(object sender, EventArgs e)
        {
    
        }
    
        void cmdForm1_Click(object sender, EventArgs e)
        {
    
        }
    
        void cmdCloseForm_Click(object sender, EventArgs e)
        {
                
        }
    }
  4. Next, set the MainForm's properties of IsMdiContainer to True.
  5. Create a few forms that needs to be opened in MainForm's MDI. You can name them anything, of course, but we take these as examples:
    • Form1.cs
    • Form2.cs
    • WelcomeForm.cs

    and the codes we use to open the forms in MDI might look like this:

    void cmdForm1_Click(object sender, EventArgs e)
    {
        Form1 f1 = new Form1();
        f1.MdiParent = this;
        f1.ControlBox = false;
        f1.MaximizeBox = false;
        f1.MinimizeBox = false;
        f1.WindowState = FormWindowState.Maximized;
        f1.Show();
    }
  6. These forms run normally, but you will notice there is an annoying Control Box appearing at the top of the Ribbon Bar control.
  7. To get rid of the Control Box, we need to rearrange these codes in the correct sequence.
  8. f1.ControlBox = false;
    f1.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None
    f1.MaximizeBox = false;
    f1.MinimizeBox = false;
    f1.WindowState = FormWindowState.Maximized;
  9. First, we create another form named MdiChildForm.cs. Open the designer for MdiChildForm.
  10. Add the below code to MdiChildForm.Designer.cs at the right sequence:
  11. this.WindowState = System.Windows.Forms.FormWindowState.Normal;
    this.ControlBox = false;
    this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
    this.MaximizeBox = false;
    this.MinimizeBox = false;

    In the Load event of MdiChildForm, add this:

    public partial class MdiChildForm : Form
    {
        public MdiChildForm()
        {
            InitializeComponent();
            this.Load += new System.EventHandler(this.MdiChildForm_Load);
        }
    
        private void MdiChildForm_Load(object sender, EventArgs e)
        {
            this.ControlBox = false;
            this.WindowState = FormWindowState.Maximized;
            this.BringToFront();
        }
    }
  12. Save and close MdiChildForm.cs and MdiChildForm.Designer.cs.
  13. Modify all forms (forms that will be loading in MainForm.cs's MDI) to inherit MdiChildForm.
  14. Form1.cs

    Change this:

    public partial class Form1 : Form

    to this:

    public partial class Form1 : MdiChildForm

    Form2.cs

    Change this:

    public partial class Form2: Form

    to this:

    public partial class Form2: MdiChildForm

    WelcomForm.cs

    Change this:

    public partial class WelcomForm: Form

    to this:

    public partial class WelcomForm: MdiChildForm
  15. Open forms and load it into the MDI client of MainForm.
  16. public partial class MainForm : Form
    {
        MdiClient mdi = null;
        public Form1()
        {
            InitializeComponent();
            foreach (Control c in this.Controls)
            {
                if (c is MdiClient)
                {
                    mdi = (MdiClient)c;
                    break;
                }
            }
    
            cmdCloseForm.Click += new EventHandler(cmdCloseForm_Click);
            cmdForm1.Click += new EventHandler(cmdForm1_Click);
            cmdForm2.Click += new EventHandler(cmdForm2_Click);
            cmdWelcome.Click += new EventHandler(cmdWelcome_Click);
        }
    
        private void LoadForm(object form)
        {
            foreach (Form f in mdi.MdiChildren)
            {
                f.Close();
            }
            if (form == null)
                return;
            ((Form)form).MdiParent = this;
            ((Form)form).Show();
        }
    
        void cmdWelcome_Click(object sender, EventArgs e)
        {
            LoadForm(new WelcomForm());
        }
    
        void cmdForm2_Click(object sender, EventArgs e)
        {
            LoadForm(new Form2());
        }
    
        void cmdForm1_Click(object sender, EventArgs e)
        {
            LoadForm(new Form1());
        }
    
        void cmdCloseForm_Click(object sender, EventArgs e)
        {
            LoadForm(null);
        }
    }
  17. Done

Part 5: Alternative Ribbon  

You may also want to have a look at: 


Part 6: How to Make a New Theme, Skin for this Ribbon Programmatically 

Default Theme

Example color theme of RibbonProfesionalRendererColorTableBlack.cs (ready made by ribbon author).

Another custom theme

 

Note: A Theme Builder is included in the Demo App, you can obtain it at Download. You can Build new Theme easily with Theme Builder. In new released, Ribbon (13 Jan 2013), Ribbon can write and read a theme file. Read more: How to Create and Load Theme File. 
  1. To make your own color theme, create another class and inherit RibbonProfesionalRendererColorTable. 
  2. Change all the color objects into your desired colors. 
  3. Example: (the first five colors have been filled for your reference).
  4. In this example, we'll name the new theme MyCoolThemeSkin.

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Drawing;
    
    namespace System.Windows.Forms
    {
        public class MyCoolThemeSkin
            : RibbonProfesionalRendererColorTable
        {
            public MyCoolThemeSkin()
            {
                #region Fields
    
                OrbDropDownDarkBorder = Color.Yellow;
                OrbDropDownLightBorder = Color.FromKnownColor(KnownColor.WindowFrame);
                OrbDropDownBack = Color.FromName("Red");
                OrbDropDownNorthA = FromHex("#C2FF3D");
                OrbDropDownNorthB = Color.FromArgb(201, 100, 150);
                OrbDropDownNorthC = 
                OrbDropDownNorthD = 
                OrbDropDownSouthC = 
                OrbDropDownSouthD = 
                OrbDropDownContentbg = 
                OrbDropDownContentbglight = 
                OrbDropDownSeparatorlight = 
                OrbDropDownSeparatordark = 
    
                Caption1 = 
                Caption2 = 
                Caption3 = 
                Caption4 = 
                Caption5 = 
                Caption6 = 
                Caption7 = 
    
                QuickAccessBorderDark = 
                QuickAccessBorderLight = 
                QuickAccessUpper = 
                QuickAccessLower = 
    
                OrbOptionBorder = 
                OrbOptionBackground = 
                OrbOptionShine = 
    
                Arrow = 
                ArrowLight = 
                ArrowDisabled = 
                Text = 
    
                RibbonBackground = 
                TabBorder = 
                TabNorth = 
                TabSouth = 
                TabGlow = 
                TabText = 
                TabActiveText = 
                TabContentNorth = 
                TabContentSouth = 
                TabSelectedGlow = 
                PanelDarkBorder = 
                PanelLightBorder = 
                PanelTextBackground = 
                PanelTextBackgroundSelected = 
                PanelText = 
                PanelBackgroundSelected = 
                PanelOverflowBackground = 
                PanelOverflowBackgroundPressed = 
                PanelOverflowBackgroundSelectedNorth = 
                PanelOverflowBackgroundSelectedSouth = 
    
                ButtonBgOut = 
                ButtonBgCenter = 
                ButtonBorderOut = 
                ButtonBorderIn = 
                ButtonGlossyNorth = 
                ButtonGlossySouth = 
    
                ButtonDisabledBgOut = 
                ButtonDisabledBgCenter = 
                ButtonDisabledBorderOut = 
                ButtonDisabledBorderIn = 
                ButtonDisabledGlossyNorth = 
                ButtonDisabledGlossySouth = 
    
                ButtonSelectedBgOut = 
                ButtonSelectedBgCenter = 
                ButtonSelectedBorderOut = 
                ButtonSelectedBorderIn = 
                ButtonSelectedGlossyNorth = 
                ButtonSelectedGlossySouth = 
    
                ButtonPressedBgOut = 
                ButtonPressedBgCenter = 
                ButtonPressedBorderOut = 
                ButtonPressedBorderIn = 
                ButtonPressedGlossyNorth = 
                ButtonPressedGlossySouth = 
    
                ButtonCheckedBgOut = 
                ButtonCheckedBgCenter = 
                ButtonCheckedBorderOut = 
                ButtonCheckedBorderIn = 
                ButtonCheckedGlossyNorth = 
                ButtonCheckedGlossySouth = 
    
                ItemGroupOuterBorder = 
                ItemGroupInnerBorder = 
                ItemGroupSeparatorLight = 
                ItemGroupSeparatorDark = 
                ItemGroupBgNorth = 
                ItemGroupBgSouth = 
                ItemGroupBgGlossy = 
    
                ButtonListBorder = 
                ButtonListBg = 
                ButtonListBgSelected = 
    
                DropDownBg = 
                DropDownImageBg = 
                DropDownImageSeparator = 
                DropDownBorder = 
                DropDownGripNorth = 
                DropDownGripSouth = 
                DropDownGripBorder = 
                DropDownGripDark = 
                DropDownGripLight = 
    
                SeparatorLight = 
                SeparatorDark = 
                SeparatorBg = 
                SeparatorLine = 
    
                TextBoxUnselectedBg = 
                TextBoxBorder = 
    
                #endregion
            }     
    
            public Color FromHex(string hex)
            {
                if (hex.StartsWith("#"))
                    hex = hex.Substring(1);
    
                if (hex.Length != 6) throw new Exception("Color not valid");
    
                return Color.FromArgb(
                    int.Parse(hex.Substring(0, 2), system.Globalization.NumberStyles.HexNumber),
                    int.Parse(hex.Substring(2, 2), System.Globalization.NumberStyles.HexNumber),
                    int.Parse(hex.Substring(4, 2), System.Globalization.NumberStyles.HexNumber));
            }   
        }
    }
  5. Then, in the Load event of MainForm.cs, add this line:
  6. namespace RibbonDemo
    {
        public partial class MainForm : RibbonForm
        {
            public MainForm()
            {
                InitializeComponent();
                ChangeTheme();
            }
    
            private void ChangeTheme()
            {
                (ribbon1.Renderer as RibbonProfessionalRenderer).ColorTable = 
                    new MyCoolThemeSkin();
                ribbon1.Refresh();   
            }
        }
    }

Part 7: Known Issues 

  • Windows 7 (Aero only) 
    • If the RibbonForm contains any control of DockStyle.Fill or DockStyle.Top the layout is wrong
      • Download the RibbonForm_beta.zip for a preliminary solution

저작자 표시 비영리 변경 금지
신고
5 4
Software Architect/C#


개발환경
- Visual Studio 2005 (SP1)
- Windows Vista Home Premium K (32-bit)




폼 디자인...
뭐 특별한건 없고 그냥 간단간단 설정좀 했습니다.
textBox1 만 있고 나머지는 그냥 배치만...

그림에 빠졌는데 반복횟수란 글자는 레이블(Label) 컨트롤 배치해 놓은것 입니다.


윗 부분에 쓰레드 사용을 위한 네임 스페이스를 지정해 줍니다.


Form1 클래스의 상단에 프로그램 전체에서 사용될 변수를 선언해 줍니다.
여기서는 쓰레드 3개를 사용하는 것으로 합니다.


Form을 더블클릭 하면 폼의 로드 이벤트로 넘어갑니다.
여기서 반복 횟수 입력 상자에 디폴트로 지정된 반복 횟수(th_Count)를 출력해 줍니다.

textBox1 에 실제로 내용을 출력해 주는 부분입니다.
쓰레드 내에서 이 함수에 값을 넘기면, textBox1에 내용을 추가하고 줄 바꿈 하는 형식으로 되어있습니다.

쓰레드가 반복횟수를 다 채우게 되면 하게 될 작업 입니다.

디자인 폼에서 button1 을 더블클릭 하면 button1의 클릭 이벤트로 넘어옵니다.
여기서 기본적인 셋팅과 함께 각 쓰레드들을 시작 시키는 일을 합니다.

- 현재의 반복횟수를 나타내는 변수 i 를 0으로 초기화 시킵니다.
- th_Count(반복횟수 지정)변수에 textBox2 에 입력된 숫자값을 입력합니다. (문자이면 ?)
- textBox1 을 클리어 시킵니다. (이전에 동작된 내용을 삭제)
- 쓰레드 동작중에 쓰레드 시작을 하게되는 button1 과 반복횟수 입력 되는 textBox2 를 건드리지 못하도록 Disable 시킵니다.

* textBox2 에 문자값이 들어가더라도 걱정할 이유는 없습니다.
소스코드에 보면 button1_Click 이벤트의 제일 첫 줄에 try 가 쓰이고 있습니다.
기본적인 형태는
try

//작업내용...

catch { } 
입니다.

try 구문에 있는 작업 내용을 실행하되, 중간에 오류가 발생하면 그 시점에서 catch 구문으로 던져버립니다.
그런데 catch 에서 할 내용은 아무것도 없이 비워놓았으니 그냥 끝나버리게 됩니다.
만약 try catch 없이 그냥 본 내용이 들어갔을 경우, 문자값이 textBox2에 들어가면 뭔가 오류 메시지 박스가 뜨겠지요;;

경우에 따라서 try catch 에서 catch 구문에 친절하게 오류발생시 작업내용(오류 알림 상자... 등)을 작업하여도 됩니다.

쓰레드 th1, th2, th3 에서 수행할  AA(), BB(), CC() 입니다.
내용은 같습니다.
다만 출력할 때, 어느 쓰레드에서 작업한 내용인가를 알려주기 위한 문자값과, 쓰레드 시작 플래그 값만 조금 다를 뿐입니다.

- 일단 쓰레드가 시작되면 i의 값을 1 증가 시킵니다.
- 그리고 문자형 변수에 i의 값을 기록하고
- sPrint 함수에 현재 쓰레드를 알려줄 문자와 함께, 그 값을 던져 textBox1 에 출력 되게 합니다.
- i 의 값이 반복횟수 카운터(th_Count)의 값 이상이 되면 Reset_Ctrl() 함수를 호출하여 쓰레드를 중단 시킵니다.

폼이 종료 될때 해야 할 일을 기술하기 위해 Closing 이벤트를 생성해야 합니다.

폼 디자인 에서 폼을 선택한 후, 속성창에 보면 번개모양(이벤트) 아이콘이 있습니다.
여기서 FormClosing 부분을 찾은다음, 옆부분에 커서가 위치한 부분의 공란을 더블클릭 하여 줍니다.

그리하면, Form1 의 Closing 이벤트로 넘어옵니다.
여기서 할일은 쓰레드를 중단 하는것 입니다.

폼(Form1)이 종료될 때 쓰레드의 시작 플래그를 false 상태로 만들고, 쓰레드를 중단시킵니다.

컴파일 후, 실행하면 이렇게 나옵니다.
여러 개의 쓰레드가 동시에 동작을 하면서 sPrint()함수 에서 미처 줄바꿈을 실행하기도 전에 다른 쓰레드로 부터 값이 마구 들어와서 생기는 문제입니다.

다음과 같이 수정해 봅시다.
textBox1에 문자 기록하고 줄바꿈 하는 작업에 lock()을 씌워 봅시다.

그리고 다시 실행을 하면...
이제야 뭔가 형태를 갖춰서 실행이 됩니다.

lock() 을 걸게 되면 lock()이 풀릴때 까지 그 내부 코드에 다른 코드가 간섭을 못하게 잠금처리 해주는거 같아보이는 군요...

그런데... 순서가 좀 이상하죠? 숫자의 순서가 뒤죽박죽 입니다.
그럼 다른 부분에 잠금 처리를 하여 보겠습니다.

일단 sPrint 부분의 lock() 을 다시 제거 하여 줍니다.

변수 i 의 값을 증가 시키고 출력 시키는 부분에 lock()를 걸어 봅시다.
여기서는 AA() 의 수정된 코드만 표시합니다만, 실제로 BB()와 CC()도 이렇게 수정하여야 합니다.

변수 i의 값을 증가 시키고, 문자형으로 변환, 출력 하는 부분을 lock 으로 감싸서 도중에 다른코드로 부터 간섭을 받지 않도록 수정하였습니다.

이제 실행하게되면...
숫자가 정상적으로 순서대로 출력되는 것을 볼 수 있습니다.

숫자 앞의 쓰레드 문자는 랜덤 입니다.
실행 할때 마다 다른 쓰레드가 뜬다는거죠...

근데 한가지... 300 번 반복 시켰는데 302 ??

여기 올리지는 않았지만 쓰레드 1개를 제거하고 다시 돌리니 301 까지 뜨는것으로 봐서 쓰레드 3개 가 동시에 돌다가 중단 처리 하는 과정에 나머지 2개가 미처 실행중이던 내용을 기록하는게 아닌가 추측해 봅니다.



*** 위에서 AA(), BB(), CC() 부분은 내용이 거의 비슷비슷 합니다.
이것을 좀더 간단하게 만들어 봤습니다.
th_work() 함수를 작성하여 그것을 호출하였습니다.

실컷 다 만들어 놓고 다 써놓고 나서 생각나서 걍 생각나는대로 끄적여 봤습니다. ^^
저렇게 바꿔도 결과는 동일 합니다.

크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by 해비

저작자 표시 비영리 변경 금지
신고
1 0
Software Architect/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);

   

  •    

신고
1 0
Software Architect/C#

if (System.ComponentModel.LicenseManager.UsageMode == LicenseUsageMode.Runtime)


간단하죠^^?


저작자 표시 비영리 변경 금지
신고
0 0
Software Architect/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);

   

  •    

신고
1 0
Software Architect/C#

출처 : 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 : 하지만 "비동기적으로 변경 될 수 있는 값"이라는 말은 아직도 이해가 되지 않는다. 한글이 영어보다 어려운건지. 저 위의 링크에서는 "비동기적으로 변경" 이라는 영어 단어는 볼 적이 없는 것 같은데... 신기할 따름이다.


신고
2 0
Software Architect/C#

출처 : 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();
        }
    }
}




신고
1 0
Software Architect/C#

출처 : 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 같은 정수 계열 형식

  • 정수 계열 형식을 기반으로 한 열거형

  • 참조 형식으로 알려진 제네릭 형식 매개 변수

  • IntPtr 및 UIntPtr

volatile 키워드는 클래스 또는 구조체의 필드에만 적용할 수 있습니다. 지역 변수는 volatile로 선언할 수 없습니다.

다음 예제에서는 공용 필드 변수를 volatile로 선언하는 방법을 보여 줍니다.

class VolatileTest
{
    public volatile int i;

    public void Test(int _i)
    {
        i = _i;
    }
}


신고
2 0
Software Architect/C#
- log4j를 사용하였지만 대부분의 로깅 프레임워크에 통용된다.
- 설정된 레벨이하가 로깅된다.
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.}
ALL
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


신고
1 0
Software Architect/C#

싱글턴 패턴(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)

위에서 제기한 문제를 해결하기 위해서는 다음 세가지의 해법을 사용할 수 있다.

  1. 인스턴스를 필요할 때 생성하지 않고, 처음부터 인스턴스를 만들어 버린다. 다시 말해서 lazy instantiation을 포기하고 static 멤버필드를 사용항여 언과 동시에 초기화하는 것이다.  단, 인스턴스를 미리 만들어 버리게 되면, 특히 해당 인스턴스가 자원을 많이 차지하는 컴포넌트일 경우에는 시스템 리소스가 쓸데없이 낭비될 가능성이 있다.
  2. getInstance() 메서드(C#의 경우 Instance 속성)를 동기화시킨다. 단, 동기화시키고자할 때는 getInstance()의 속도가 그렇게 중요하지 않다고 판단될 경우이며 동기화로 인한 오버헤드를 감수해야 한다. - 메서드를 동기화 시키면 일반적으로 성능이 100배 정도는 저하된다고 한다.
  3. 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



출처 : http://kyungseo.pe.kr/blog/111

신고
0 1
Software Architect/C#

출처 : 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

신고
1 0
Software Architect/C#

출처 : 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 순서이다. 

 Form1.cs

예제로 작성된 Source 파일

                                 < 예제로 작성된 .cs를 참고 하자 > 

                                 < 예제로 작성된 설정파일을 참고 하자 > 
                              

손쉬운 프로그램 로그 Library로 Log Library 작성하는 삽질은 피해보자.


신고
0 0
Software Architect/C#

출처 : 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)

신고
1 0
Software Architect/C#
출처 :  http://blog.naver.com/PostView.nhn?blogId=siro012&logNo=60087821616&redirect=Dlog&widgetTypeCall=true 


신고
2 0
Software Architect/C#

출처 : 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를 통해 자식폼의 텍스트 박스에 적힌 텍스트를 가져올 수 있네요;

다시, 정리하자면 이렇습니다.
1. 이벤트는 이벤트를 발생시키는 곳이 있으면, 발생한 이벤트를 듣는 곳도 있다;
2. 이벤트는 이벤트를 발생시키는 곳과 발생한 이벤트를 듣는 곳을 잇기 위한 대리자(delegate)로 구현한다.

delegate란 개념은 단지; 이벤트를 구현할 때만 쓰이지 않습니다; 제가 오래 전에 블로그에 쓴 C#에서 크로스 쓰레드 문제에서도 언급된 적이 있습니다. 사실 저도 저 때 delegate 개념이 구체화 되지는 않았습니다; 어렴풋이 이해만 했었고; 단지 저런 상황이 생기면; 다시 써먹기 위해 남겼을 뿐이지요..;

혹시나 필요하신 분들이 있을까; 아티클에 설명한 예제의 소스 코드를 첨부합니다;

다운로드

신고
2 0