블로그 이미지

[C#] ns(nano seconds) 나노 초 단위의 시스템 성능 측정 C# 코드

2013. 7. 24. 11:32

C#에서

DateTime을 활용하면 ms 단위의 시간 밖에 측정 할 수 없다.

하지만, StopWatch 의 CPU Ticks 값, CPU 주파수(System.Frequency) 을 이용하면 ns(나노 초 )단위로 시스템 성능을 측정할 수 있다.


예제 코드는 아래와 같다.



 StopWatch 를 이용한 ns 단위 시스템 성능 측정

using System; using System.Threading; using System.Diagnostics; //StopWatch; class MainApp { static void Main(string[] args) { long StartTime, EndTime; Stopwatch SystemPerformanceWatch = new Stopwatch(); SystemPerformanceWatch.Start(); StartTime = SystemPerformanceWatch.ElapsedTicks; //성능 측정 코드 삽입 부분 Thread.Sleep(1000); EndTime = SystemPerformanceWatch.ElapsedTicks; Console.WriteLine("연산소모 시간 {0}", String.Format("{0:#,###.###} ns", (1000 * 1000 * 1000 * (EndTime - StartTime) / Stopwatch.Frequency))); } }



블로그 이미지

C#에서 C++ DLL 사용하기

2013. 6. 29. 03:56

C#에서 C++ DLL 사용하기

C++ DLL return char *, C++ DLL 문자열 인자 전달


예제에서 사용된 DLL은 Unmanaged C++(관리되지 않은 C++)로 만들어졌다.

예제에서는 Static DLL 호출 방법을 사용하였다.

예제는 (Visual Studio 2012) 비주얼 스튜디오 2012에서 작성하였다.


[새 프로젝트]-[Visual C++]-[Win32 프로젝트]

솔루션이름 : JINGUDLL

응용프로그램 종류 : DLL

추가 옵션 : 빈 프로젝트

 jingudll.cpp

#include <stdio.h>
#include <string.h>
#include <windows.h>		//LocalAlloc, LPTR

#define EXPORTDLL extern "C" __declspec(dllexport)

EXPORTDLL char* getMessage();
EXPORTDLL void copyMessage(char * _input, char * _output);
extern "C" void printMessage(char* _input);

//dll 내부의 모든 코드는 extern "C" 안에 정의 되어야 한다.
extern "C"
{
	//C#에서 불러다 쓸 수 없다.
	//dll 내부에서만 호출이 가능하다.
	char * result = "C++ Dll로부터 응답"; 

	void printMessage(char * _input)
	{
		printf("'%s' is came from C# \n",_input);
	}
}

EXPORTDLL char* getMessage()
{
	char * returnchar = (char*)LocalAlloc(LPTR, strlen(result)+1);

	strcat(returnchar, result);
	return returnchar;
}

EXPORTDLL void copyMessage(char * _input, char * _output)
{
	printMessage(_input);
	strcpy(_output, _input);
}


위의 소스를 빌드 후 프로젝트 폴더의 경로에 가보면 "JINGUDLL.dll" 파일이 생성되어 있다. 이 파일을 아래에 새롭게 생성한 C# 프로젝트 내의 실행 파일과 같은 경로에 파일을 복사한다.



[새프로젝트] - [Visual C#] - [콘솔 응용프로그램]

 Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;

namespace DLLTEST
{
    class Program
    {
        [DllImport("JINGUDLL.dll", CallingConvention =CallingConvention.Cdecl)]
        private static extern IntPtr getMessage();

        [DllImport("JINGUDLL.dll", CallingConvention = CallingConvention.Cdecl)]
        private static extern void copyMessage(StringBuilder _input, StringBuilder _output);

        static void Main(string[] args)
        {
            IntPtr ptr = getMessage();
            string Message = Marshal.PtrToStringAnsi(ptr);
            Marshal.FreeHGlobal(ptr);
            Console.WriteLine("C++ Dll로 부터 리턴된 char* : {0}", Message);

            StringBuilder _input = new StringBuilder("This is value");
            StringBuilder _output = new StringBuilder();
            copyMessage(_input, _output);
            Console.WriteLine("C++ DLL 내에서 변경된 _output의 값은 {0}",_output);
        }
    }
}


C, C++언어에서의 char * 에 대응 되는 C#의 자료형은 String이 아니다. StringBuilder 이다.

C, C++언어에서의 char * 리턴을 받을 때 주소값을 저장할 수 있는 IntPtr 자료형을 사용하여야 한다.

DllImport시에 CallingConvention =CallingConvention.Cdecl 을 꼭 선언해주어야 한다.

DLL에서 LocalAlloc으로 할당한 메모리는 C#에서 Marshal.FreeGlobal()로 제거해 주어야 한다.

  1. 2015.02.02 21:10  address  modify / delete  reply

    비밀댓글입니다


블로그 이미지

[.NET] CLR(Common Language Runtime, 공통 언어 실행 환경)

2013. 6. 7. 11:47



  • CLR : Common Language Runtime(공통 언어 실행 환경)

CLR은 마이크로소프트가 Common Language Infrastructure (CLI) 표준을 토대로 구현한 것이며 .NET Framework에 있는 가상 머신 컴포넌트이다. .NET 환경에서 컴파일은 CLR 컴파일을 의미한다.


  • JIT (Just In Time) 컴파일 : 

실행에 필요한 코드를 실행할 때마다 실시간으로 네이티브 코드로 번역해서 사용한다.

.NET 컴파일러(== CLR 컴파일러)는 .NET 소스 코드를 읽어서 Intermediate Language라는 중간 언어로 된 파일을 만들어 냅니다.

사용자가 이 파일을 시작하면 JIT가 Intermediate Language를 읽어들여서 네이티브 코드(원시 기계어)로 변역한 뒤, 실행시킨다. 이 방식을 JIT(Just In Time) 컴파일이라고 부른다.

일단 JIT 컴파일이된 코드는 캐시에 저장된다. 응용 프로그램이 종료되면 컴파일된 코드는 시스템에서 제거된다. 전체 프로그램의 컴파일은 한번에 이루어지지 않고 필요한 순간에 필요한 코드 부분에 대해서만 컴파일 된다. 


또한 CLR은 C# 등 .NET언어를 동작시키는 기능 뿐만 아니라, 



  1. exception handling : 프로그램의 오류(예외)가 발생 했을 때, 이를 처리하도록 도와주는 기능도 있습니다.
  2. memory management : 가비지 컬렉션
  3. type safety : 

등 3가지 추가 서비스 기능을 제공한다.


블로그 이미지

[C#] lock 블럭

2013. 6. 2. 23:18

출처 : http://csharpstudy.com/Threads/lock.aspx

아래 예제는 여러 개의 스레드가 Thread-Safe 하지 않은 메서드를 호출하는 예를 보여주고 있다. 10개의 스레드가 counter라는 필드를 동시에 쓰거나 읽는 샘프롤서 한 스레드가 counter변수를 변경하고 읽기 전에 다른 스레드가 다시 counter변수를 변경할 수 있기 때문에 불확실한 결과를 출력하게 된다.


예제

using System;
using System.Threading;

namespace MultiThrdApp
{
    class MyClass
    {
        private int counter = 1000;

        public void Run()
        {
            // 10개의 쓰레드가 동일 메서드 실행
            for (int i = 0; i < 10; i++)
            {
                new Thread(UnsafeCalc).Start();    
            }
        }

        // Thread-Safe하지 않은 메서드 
        private void UnsafeCalc()
        {
            // 객체 필드를 모든 쓰레드가 
            // 자유롭게 변경
            counter++;

            // 가정 : 다른 복잡한 일을 한다
            for (int i = 0; i < counter; i++)
                for (int j = 0; j < counter; j++) ;

            // 필드값 읽기
            Console.WriteLine(counter);
        }
    }
}



C# lock 키워드

C#의 lock 키워드는 특정 블럭의 코드(Critical Section이라 부른다)를 한번에 하나의 스레드만 실행할 수 있도록 해준다. lock()의 파라미터에는 임의의 객체를 사용할 수 있는데, 주로 object타입의 private 필드를 지정한다. lock(this)와 같이 클래스 객체 전체를 지정하는 this를 사용할 수도 있는데, 이는 불필요하게 모든 클래스 객체를 잠그는 효과가 있으므로, object타입의 필드를 만들어 사용하는 것이 좋다. Critical Section 코드 블록은 가능한 한 범위를 작게하는 것이 좋은데, 이는 필요한 부분만 Locking한다는 원칙에 따른 것이다.


예제

using System;
using System.Threading;

namespace MultiThrdApp
{
    class MyClass
    {
        private int counter = 1000;

        // lock문에 사용될 객체
        private object lockObject = new object();

        public void Run()
        {
            // 10개의 쓰레드가 동일 메서드 실행
            for (int i = 0; i < 10; i++)
            {
                new Thread(SafeCalc).Start();    
            }
        }

        // Thread-Safe 메서드 
        private void SafeCalc()
        {
            // 한번에 한 쓰레드만 lock블럭 실행
            lock (lockObject)
            {
                // 필드값 변경
                counter++;

                // 가정 : 다른 복잡한 일을 한다
                for (int i = 0; i < counter; i++)
                    for (int j = 0; j < counter; j++) ;

                // 필드값 읽기
                Console.WriteLine(counter);
            }
        }

        //출력 예:
        // 1001
        // 1002
        // 1003
        // 1004
        // 1005
        // 1006
        // 1007
        // 1008
        // 1009
        // 1010
    }
}


'컴퓨터 > C#' 카테고리의 다른 글

C#에서 C++ DLL 사용하기  (1) 2013.06.29
[.NET] CLR(Common Language Runtime, 공통 언어 실행 환경)  (0) 2013.06.07
[C#] lock 블럭  (1) 2013.06.02
[C#] 객체와 생성  (0) 2013.05.28
[C#] get, set 함수  (0) 2013.01.18
[C#] TCP, UDP MultiThread Client Source Code  (0) 2013.01.07
  1. devming 2016.10.31 13:09 신고  address  modify / delete  reply

    감사합니다 참고할게요! ㅎㅎ


블로그 이미지

[C#] 객체와 생성

2013. 5. 28. 15:03

객체와 메모리

* 객체 생존 기간
객체는 new 연산자에 의해 메모리를 할당 하고 생성자에 의해 메모리에 있는 객체가 초기화 되는 것입니다. 반대로 소멸되는 경우에는 먼저 Finalize 메소드를 이용하여 메모리를 초기화 되지 않은 상태로 돌리며 다음으로 이메모리 공간을 Heap에 반환하는 것입니다. 그러므로 객체의 존속기는 new를 이용하여 메모리를 할당받는 순간부터 메모리를 Heap에 반환 할 때 까지라고 보면 되겠습니다.

* 객체와 영역
int나 struct와 같이 Stack에 존재하는 Value Type 변수들은 범위를 벗어나면 사라지므로 존재 기간이 짧습니다. 그러나 Heap에 있는 객체(Reference Type)들인 경우 영역을 벗어 나더라도 메모리를 해제 할때까지 사라지지 않는 것이 특징 입니다.

* GC(Garbage Collector)
C#의 경우 기존의 C/C++ 처럼(new, delete) 프로그래머가 메모리 관리를 하지 않아도 되게 설계가 됭 있습니다.(자바와 비슷하죠^^;;;) CLR에서 자동으로 알아서소멸 시켜 줍니다. 또한 C#의 경우 명시적으로 코드상에서 객체를 소멸 할 수 없는 대신 Garbage Collector라는 것을 지원 해 줍니다. Garbage Collector는 메모리에 있는 참조가 끝난 객체를 쓰레기 치우는 것처럼 소멸 시키는 역할을 하는 것인데 메모리가 부족 하다고 판단이 들면 Garbage Collector는 참조되는 않는 객체의 메모리 영역을 청소하여 Heap에 반환 하는 것입니다. 또한 Garbage Collector는 객체를 오직 한번만 제거해 버리며 참조되고 있지 않은 것들만 제거를 합니다.(만약에 참조되고 있는 객체를 쓰레긴줄 알고 버리면 큰일 나겠죠???). 프로그래머가 일일이 코드를 통해 기술 했을때의 문제인 객체를 반복해서 소멸 한다든지, 참조되고 있는 객체를 소멸한다든지 하는것은 막아 주는 것입니다. 코딩 상에서 Garbage Collector에게 명시적으로 객체를 소멸 시켜달라고 할 수 있으나 그것은 객체를 소멸 시켜도 된다는 것을 알리는 역할을 할 뿐이지 즉시 작동 하는 것은 아닙니다.

* 소멸자의 사용
- Finalize 메소드
객체가 Garbage Collector에 의해 소멸되는 시점에 호출되는 함수 이다. 이곳에 파일을 닫는 다든지, DB접속을 종료 한다든지 하는 일들을 명시 할 수 있습니다. Finalize 메소드를 구현 하면 C# 컴파일러가 컴파일시 finalization Queue에 이 객체가 등록되고 Garbage Collector가 작동되면 객체를 소멸하기 전에 Finalize 메소드를 호출되는 것입니다.

- 소멸자 사용방법
Finalize 메소드 대신에 소멸자(~생성자)를 이용 할 수 있으며 컴파일을 하게 되면 소멸자를 Finalize 메소드로 바꾸어 놓게 됩니다. 즉 C#에서 소멸자와 Finalize 메소드는 같은 의미라고 보면 됩니다. 소멸자의 경우 항상 접근권한이 public이 되어야 하므로 별도로 접근 지시자를 지정하지 못하도록 되어 있습니다. 또한 매개변수도 받을 수 없습니다.

* System.CG 클래스
Garbage Collector가 작동 하기 전에 리소스를 해제 할려고 하면 어떻게 할까?... 이경우 IDisposal 인터페이스의 Dispose 메소드를 구현해 주고 사용자가 이메소드를 호출하면 직접 리소스를 해제 할 수 있습니다. 물론 Disposal 메소드안에 리소스를 정리하는 코드를 기술하면 되는거죠... 만약 리소스를 해제 하는 코드가 소멸자에도 있고 Dispoase에도 있으면 코드가 중복되게 되는데... 소멸자에서도 Dispose 메소드를 호출 함으로서 리소스의 해제가 가능 합니다. 만약 Dispose 메소드를 이용하여 리소스를 해제 하였다면 Finalize 메스에서는 리소스를 해제 하지 않아도 되는데 이런경우 System.GC 클래스의 SuppressFinalize 메소드를 호출하여 Finalize 메소드가 호출되지 않게 설정 할 수 있다. 결국 아래의 소스와 같은 구조가 되는 것이다.

using System; 
class Test : IDisposable { 
    private bool isDispose = false; 
    ... 
    ~Test() { 
        if (!isDispose) { 
            Dispose(); 
        } 
    } 

    public void Dispose() { 
        isDispose = true; 
        // 이 부분에서 리소스를 정리 한다. 
        // 참고로 GC.Collect() 메소드는 Garbage Collector가 Finalize를 호출하게 하는 메소드 이다. 
        GC.SuppressFinalize(this); //Finalize 메소드를 소멸자에서 호출 안하도록 설정 
    } 
} 

[예제]
using System; 
class Garbage : IDisposable 
{ 
    private bool isDispose = false; 
    private string name; 

    public Garbage(string name) 
    { 
        this.name = name; 
        Console.WriteLine("{0}객체 생성됨...", this.name); 
    } 

    ~Garbage() 
    { 
        if (!isDispose) 
        { 
            Dispose(); 
        } 
    } 

    public void Dispose() 
    { 
        isDispose = true; 
        //... 리소스를 해제함... 
        Console.WriteLine("{0}객체의 리소스 해제 OK...", name); 
        GC.SuppressFinalize(this); 
    } 
} 

class GarbageTest1 
{ 
    static void Main() 
    { 
        Garbage g1 = new Garbage("1번객체"); 
        Garbage g2 = new Garbage("2번객체"); 
        Garbage g3 = new Garbage("3번객체"); 
        Garbage g4 = new Garbage("4번객체"); 

        g1.Dispose(); 
        GC.SuppressFinalize(g2); 
    } 
}
[결과]
1번객체객체 생성됨...
2번객체객체 생성됨...
3번객체객체 생성됨...
4번객체객체 생성됨...
1번객체객체의 리소스 해제 OK...
4번객체객체의 리소스 해제 OK...
3번객체객체의 리소스 해제 OK...

'컴퓨터 > C#' 카테고리의 다른 글

[.NET] CLR(Common Language Runtime, 공통 언어 실행 환경)  (0) 2013.06.07
[C#] lock 블럭  (1) 2013.06.02
[C#] 객체와 생성  (0) 2013.05.28
[C#] get, set 함수  (0) 2013.01.18
[C#] TCP, UDP MultiThread Client Source Code  (0) 2013.01.07
[C#] TCP, UDP MultiThread Server Source Code  (0) 2013.01.07


블로그 이미지

[C#] get, set 함수

2013. 1. 18. 11:41

이번에 소개해드릴 내용은 기존 C++에서는 없던 C#에 나오는 get 과 set 키워드입니다.

보통 C++의 경우 클래스 내부 private 맴버변수( 캡슐화된 )의 값에 접근하기 위해서

대략 아래와 같이 get....()과 set....()함수를 만들어 사용을 합니다.


C#에 와서는 이런 함수를 대신할 키워드인 get과 set이 기본적으로 제공이 됩니다~+_+

사용방법은 아래와 같습니다.


앞의 C++ 클래스와 동일하게 m_amount라는 값을 private로 선언했으며,

리턴 자료타입은 m_amount와 동일한 int형으로 선언하고 num이라는 함수가 아닌 키워드를 선언했습니다.

리턴 자료타입인 int는 get 키워드에서 받아오는 m_amount값을 받기위해 꼭 선언되어야하며,

num이라는 키워드를 이용해 m_amount값을 변경할 수 있습니다.

사용법은 아래와 같습니다.


item.num = 10; 은 set키워드가 호출되는 부분이며, 아래 int amount = item.num; 은 get키워드가

호출되는 부분입니다.

'컴퓨터 > C#' 카테고리의 다른 글

[C#] lock 블럭  (1) 2013.06.02
[C#] 객체와 생성  (0) 2013.05.28
[C#] get, set 함수  (0) 2013.01.18
[C#] TCP, UDP MultiThread Client Source Code  (0) 2013.01.07
[C#] TCP, UDP MultiThread Server Source Code  (0) 2013.01.07
[C#][Network] RUDP (Reliable UDP)  (0) 2013.01.02


블로그 이미지

[C#] TCP, UDP MultiThread Client Source Code

2013. 1. 7. 17:49

[C#] TCP, UDP MultiThread Client Source Code

/* Project: Simple TCP/UDP Client v2
* Author : Patrick Lam
* Date : 09/19/2001
* Brief : The simple TCP/UDP Client v2 does exactly the same thing as v1. What 
itintends
* to demonstrate is the amount of code you can save by using TcpClient and UdpClient
* instead of the traditional raw socket implementation. When you
* compare the following code with v1, you will see the difference.
* Usage : sampleTcpUdpClient2   "Any message."
* Example: sampleTcpUdpClient2 TCP localhost "hello. how are you?"
* Bugs : When you send a message with UDP, you can't specify localhost as the
* destination. Doing so will produce an exception. Can't figure out why yet. The workaround
* to use the machine's hostname instead.
*/

using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace SmartGridClient
{

    public class sampleTcpUdpClient2
    {
        public enum clientType { TCP, UDP }; //Type of connection the client is making. 
        private const int ANYPORT = 0;
        private const int SAMPLETCPPORT = 4567;
        private const int SAMPLEUDPPORT = 4568;
        private bool readData = false;
        public clientType cliType;
        private bool DONE = false;
        public sampleTcpUdpClient2(clientType CliType)
        {
            this.cliType = CliType;
        }
        public void sampleTcpClient2(String serverName, String whatEver)
        {
            try
            {
                //Create an instance of TcpClient. 
                TcpClient tcpClient = new TcpClient(serverName, SAMPLETCPPORT);
                //Create a NetworkStream for this tcpClient instance. 
                //This is only required for TCP stream. 
                NetworkStream tcpStream = tcpClient.GetStream();
                if (tcpStream.CanWrite)
                {
                    Byte[] inputToBeSent = System.Text.Encoding.ASCII.GetBytes(whatEver.ToCharArray());
                    tcpStream.Write(inputToBeSent, 0, inputToBeSent.Length);
                    tcpStream.Flush();
                }
                while (tcpStream.CanRead && !DONE)
                {
                    //We need the DONE condition here because there is possibility that 
                    //the stream is ready to be read while there is nothing to be read. 
                    if (tcpStream.DataAvailable)
                    {
                        Byte[] received = new Byte[512];
                        int nBytesReceived = tcpStream.Read(received, 0, received.Length);
                        String dataReceived = System.Text.Encoding.ASCII.GetString(received);
                        Console.WriteLine(dataReceived);
                        DONE = true;
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("An Exception has occurred.");
                Console.WriteLine(e.ToString());
            }
        }
        public void sampleUdpClient2(String serverName, String whatEver)
        {
            try
            {
                //Create an instance of UdpClient. 
                UdpClient udpClient = new UdpClient(serverName, SAMPLEUDPPORT);
                Byte[] inputToBeSent = new Byte[256];
                inputToBeSent = System.Text.Encoding.ASCII.GetBytes(whatEver.ToCharArray());
                IPHostEntry remoteHostEntry = Dns.GetHostByName(serverName);
                IPEndPoint remoteIpEndPoint = new IPEndPoint(remoteHostEntry.AddressList[0], SAMPLEUDPPORT);
                int nBytesSent = udpClient.Send(inputToBeSent, inputToBeSent.Length);
                Byte[] received = new Byte[512];
                received = udpClient.Receive(ref remoteIpEndPoint);
                String dataReceived = System.Text.Encoding.ASCII.GetString(received);
                Console.WriteLine(dataReceived);
                udpClient.Close();
            }
            catch (Exception e)
            {
                Console.WriteLine("An Exception Occurred!");
                Console.WriteLine(e.ToString());
            }
        }
        public static void Main(String[] argv)
        {
            if (argv.Length < 3)
            {
                Console.WriteLine("Usage: sampleTcpUdpClient2   Message");
                Console.WriteLine("Example: sampleTcpUdpClient2 TCP localhost ''hello. how are you?''");
            }
            else if ((argv[0] == "TCP") || (argv[0] == "tcp"))
            {
                sampleTcpUdpClient2 stc = new sampleTcpUdpClient2(clientType.TCP);
                stc.sampleTcpClient2(argv[1], argv[2]);
                Console.WriteLine("The TCP server is disconnected.");
            }
            else if ((argv[0] == "UDP") || (argv[0] == "udp"))
            {
                sampleTcpUdpClient2 suc = new sampleTcpUdpClient2(clientType.UDP);
                suc.sampleUdpClient2(argv[1], argv[2]);
                Console.WriteLine("The UDP server is disconnected.");
            }
        }
    } 
} 


'컴퓨터 > C#' 카테고리의 다른 글

[C#] 객체와 생성  (0) 2013.05.28
[C#] get, set 함수  (0) 2013.01.18
[C#] TCP, UDP MultiThread Client Source Code  (0) 2013.01.07
[C#] TCP, UDP MultiThread Server Source Code  (0) 2013.01.07
[C#][Network] RUDP (Reliable UDP)  (0) 2013.01.02
[C#] Timer 세가지와 차이점  (0) 2012.12.31


블로그 이미지

[C#] TCP, UDP MultiThread Server Source Code

2013. 1. 7. 17:48

[C#] TCP, UDP MultiThread Server Source Code



 using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;

/* Project : Simple Multi-threaded TCP/UDP Server v2
* Author : Patrick Lam
* Date : 09/19/2001
* Brief : The simple multi-threaded TCP/UDP Server v2 does the same thing as v1. What
* it intends to demonstrate is the amount of code you can save by using 
TcpListener
* instead of the traditional raw socket implementation (The UDP part is still
* the same. When you compare the following code with v1, you will see the
* difference.
* Usage : sampleTcpUdpServer2
*/

namespace SmartGridServer
{
    public class SampleTcpUdpServer2
    {
        private const int sampleTcpPort = 4567;
        private const int sampleUdpPort = 4568;
        public Thread sampleTcpThread, sampleUdpThread;
        public SampleTcpUdpServer2()
        {
            try
            {
                //Starting the TCP Listener thread.
                sampleTcpThread = new Thread(new ThreadStart(StartListen2));
                sampleTcpThread.Start();
                Console.WriteLine("Started SampleTcpUdpServer's TCP Listener Thread!\n");
            }
            catch (Exception e)
            {
                Console.WriteLine("An TCP Exception has occurred!" + e.ToString());
                sampleTcpThread.Abort();
            }
            try
            {
                //Starting the UDP Server thread.
                sampleUdpThread = new Thread(new ThreadStart(StartReceiveFrom2));
                sampleUdpThread.Start();
                Console.WriteLine("Started SampleTcpUdpServer's UDP Receiver Thread!\n");
            }
            catch (Exception e)
            {
                Console.WriteLine("An UDP Exception has occurred!" + e.ToString());
                sampleUdpThread.Abort();
            }
        }
        public static void Main(String[] argv)
        {
            SampleTcpUdpServer2 sts = new SampleTcpUdpServer2();
        }
        public void StartListen2()
        {
            //Create an instance of TcpListener to listen for TCP connection.
            TcpListener tcpListener = new TcpListener(sampleTcpPort);
            try
            {
                while (true)
                {
                    tcpListener.Start();
                    //Program blocks on Accept() until a client connects.
                    Socket soTcp = tcpListener.AcceptSocket();
                    Console.WriteLine("SampleClient is connected through TCP.");
                    Byte[] received = new Byte[512];
                    int bytesReceived = soTcp.Receive(received, received.Length, 0);
                    String dataReceived = System.Text.Encoding.ASCII.GetString(received);
                    Console.WriteLine(dataReceived);
                    String returningString = "The Server got your message through TCP: " + dataReceived;
                    Byte[] returningByte = System.Text.Encoding.ASCII.GetBytes(returningString.ToCharArray());
                    //Returning a confirmation string back to the client.
                    soTcp.Send(returningByte, returningByte.Length, 0);
                    tcpListener.Stop();
                }
            }
            catch (SocketException se)
            {
                Console.WriteLine("A Socket Exception has occurred!" + se.ToString());
            }
        }
        public void StartReceiveFrom2()
        {
            IPHostEntry localHostEntry;
            try
            {
                //Create a UDP socket.
                Socket soUdp = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                try
                {
                    localHostEntry = Dns.GetHostByName(Dns.GetHostName());
                }
                catch (Exception)
                {
                    Console.WriteLine("Local Host not found"); // fail
                    return;
                }
                IPEndPoint localIpEndPoint = new IPEndPoint(localHostEntry.AddressList[0], sampleUdpPort);
                soUdp.Bind(localIpEndPoint);
                while (true)
                {
                    Byte[] received = new Byte[256];
                    IPEndPoint tmpIpEndPoint = new IPEndPoint(localHostEntry.AddressList[0], sampleUdpPort);
                    EndPoint remoteEP = (tmpIpEndPoint);
                    int bytesReceived = soUdp.ReceiveFrom(received, ref remoteEP);
                    String dataReceived = System.Text.Encoding.ASCII.GetString(received);
                    Console.WriteLine("SampleClient is connected through UDP.");
                    Console.WriteLine(dataReceived);
                    String returningString = "The Server got your message through UDP:" + dataReceived;
                    Byte[] returningByte = System.Text.Encoding.ASCII.GetBytes(returningString.ToCharArray());
                    soUdp.SendTo(returningByte, remoteEP);
                }
            }
            catch (SocketException se)
            {
                Console.WriteLine("A Socket Exception has occurred!" + se.ToString());
            }
        }
    }
}



블로그 이미지

[C#][Network] RUDP (Reliable UDP)

2013. 1. 2. 11:02

C# RUDP(Reliable UDP)

http://rudp.codeplex.com/


블로그 이미지

[C#] Timer 세가지와 차이점

2012. 12. 31. 12:10

스크랩 : http://blog.daum.net/starkcb/117


특정 작업을 주기적으로 실행하기 위해 흔히 Timer 객체를 사용합니다

정해진 시간 간격으로 변수를 업데이트 한다던지, 모니터링 한다던지, 로그를 기록 한다던지, 그 작업 내용은 무궁무긴 하겠죠

Timer 객체는 이러한 주기적 작업을 아주 쉽게 처리해 주는, 닷넷 프레임워크에서 제공하는 고마운 객체입니다


그러나 한가지 생각해 볼 문제가 있네요..

닷넷 프레임워크에는 무려 3가지 서로 다른 Timer 를 제공하고 있다는 겁니다. 바로 아래 3가지 Timer 입니다
1. System.WIndows.Forms.Timer
2. System.Threading.Timer

3. System.Timers.Timer

닷넷이 이 3가지 Timer 를 각각 제공하는 이유가 무엇일까요?

필자는 이 문제(?)에 대해, 몇 년전에 의구심을 가졌읍니다만, 당시 의구심만 가진채 그냥 세월을 보내 버렸습니다 --;

그리고는 대략 아는 지식으로 대략 적절 할 것 같은(?) Timer을 사용해 왔던 것 같습니다

게을렀던 거죠. 의구심이 들면 파고들어 정복하는 사람이 성공합니다. ㅋㅋ , 기술이든 인생이든...

예기가 다른 길로 세네요.. ㅎ,

우선 이 세가지 서로다른 Timer 의 msdn 설명을 볼까요


1) System.Windows.Forms.Timer
사용자가 정의한 간격마다 이벤트를 발생시키는 타이머를 구현합니다. 이 타이머는 Windows Forms 응용 프로그램에서

사용할 수 있도록 최적화되었으며 창에서 사용해야 합니다

2) System.Threading.Timer

지정된 간격으로 메서드를 실행하는 메커니즘을 제공합니다

3) System.Timers.Timer

응용 프로그램에 되풀이 이벤트를 생성합니다


msdn 설명을 봐도,
'System.WIndows.Forms.Timer 가 윈도우 응용프로그램에 최적화 되었다' 라는 말 빼고는 거의 차이점을 느낄 수 없네요

물론 msdn은 보다 상세한 내용을 더 기술되어 있습니다만, 이 글에서는 이 세가지 Timer 의 차이점을 크게 두 가지 측면에서
살펴 볼까 합니다

1. 사용법상의 차이점

2. 수행되는 Thread 환경의 차이점

* 사용법의 차이

먼저 사용법의 차이를 알아보죠

사용법의 차이는 말 그대로 사용법입니다. 이것이 원리는 아니죠.
원리가 다르기 때문에 사용법이 다른 것이지, 사용법이 다르기 때문에 원리가 다른건 아닙니다

그럼에도, 사용법 차이점부터 알아 보는 것은.......... 쉽기 때문이죠 ^^;

(개발자 여러분, 사용법만 익히지 말고 원리를 익힙시다)

1. System.Windows.Forms.Timer 사용법
윈도우 응용프로그램 개발자들에겐 아마 가장 익숙한 Timer 일 것입니다

- 객체 생성

System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();

- 반복 주기 및 작업 설정

timer.Interval = 1000; //주기 설정

timer.Tick += new EventHandler(timer_Tick); //주기마다 실행되는 이벤트 등록

void tmrWindowsFormsTimer_Tick(object sender, System.EventArgs e)
{
//수행해야할 작업

}

- Timer 시작

timer.Enable = true 또는 timer.Start();

- Timer 중지

timer.Enable = false 또는 timer.Stop();

2. System.Threading.Timer 사용법

- 객체 생성

Timer 객체를 생성할 때, 반복적으로 실행하게 될 메서드를 콜백 메서드로 등록해야 합니다

System.Threading.Timer timer = new System.Threading.Timer(CallBack);

- 반복 주기 및 작업 설정

이 Timer 에는 Change 메서드가 있는데, 이 메서드는 dueTime과 period 를 입력받습니다

dueTime은 Timer 가 시작하기 전 대기(지연)시간이며 period는 반복 주기입니다
timer.Change(0, 1000);

그리고 반복 실행 작업이,
윈도우 응용프로그램의 UI Thread와 연관된다면, Cross Thread 문제가 발생하기 때문에 Invoke나 BeginInvoke를

통해 핸들링 해야 합니다.

앞서, Timer 객세 생성시 등록한 콜백 메서드에서 BeginInvoke를 통해 UI 쓰레드를 핸들링 할 수 있습니다

delegate void TimerEventFiredDelegate();

void CallBack(Object state)
{
BeginInvoke(new TimerEventFiredDelegate(Work));
}

private void Work()
{

//수행해야할 작업(UI Thread 핸들링 가능)
}

- Timer 시작

위의 Change 메서드의 dueTime 이 0 이므로 그 즉시 시작된다. Start와 같은 별도의 시작 명령이 존재하지 않음

- Timer 중지
timer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);

dueTime와 period 를 무한대로 잡아서 Timer 가 실행되지 않도록 하는 것이 중지하는 것과 같습니다

3. System.Timers.Timer 사용법

- 객체 생성

System.Timers.Timer timer = new System.Timers.Timer();

- 반복 주기 및 작업 설정
timer.Interval = 1000;
timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed); //주기마다 실행되는 이벤트 등록


이 Timer 역시 UI Thread를 핸들링 하기 위해서 Invoke 나 BeginInvoke를 이용해야 합니다
delegate void TimerEventFiredDelegate();
void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
BeginInvoke(new TimerEventFiredDelegate(Work));
}

private void Work()
{

//수행해야할 작업(UI Thread 핸들링 가능)
}

- Timer 시작

timer.Enable = true 또는 timer.Start();

- Timer 중지

timer.Enable = false 또는 timer.Stop();

결론적으로 보면,

Timer 객체의 사용법 자체는 그리 어렵지 않습니다. 또한 세 가지 Timer 의 사용법은 대동소이 함을 알 수 있습니다

다만 윈도위 응용프로그램에서 Timer 를 사용할 때 System.WIndows.Forms.Timer 를 제외하고는

UI Thread 에서 만들어진 컨트롤에 접근하려면 크로스 쓰레드 문제가 있으므로 마샬링 된 호출(Invoke / BeginInvoke) 를

이용해야 하는 차이점이 있습니다

msdn의 설명처럼 System.Windows.Forms.Timer 는 윈도우 응용프로그램에 최적화 되어 있나 보네요..

그럼 왜 System.Windows.Forms.Timer 는 크로스쓰레드 문제가 발생하지 않을까요?

그리고 정말 사용법 처럼 크게 차이가 나지 않는 걸까요?


다음에 설명할, 두번째 관점인 '수행되는 Thread 환경의 차이점'에서 이를 알아보도록 하죠

* 수행되는 쓰레드(Thread) 환경의 차이

앞서 사용법에서 UI Thread 라는 말을 했습니다

윈도우 응용프로그램을 예로 들어, 버턴이나 각종 컨트롤이 생성되고 핸들링 되는 것은 UI Thread 상에서 이루어집니다

이와 다른 개념이 Work Thread 인데요, 기본 쓰레드(Default Thread) 이외에
개발자가 별도의 쓰레드를 생성하여 작업을 실행한다면 이는 Work Thread(작업자 쓰레드) 라 합니다

또한 UI Thread 입장에서는, 닷넷의 ThreadPool 에 의해 실행되는 쓰레드도 Work Thread 로 볼 수 있습니다

쓰레드가 다르다면 쓰레드의 고유번호도 당연히 다릅니다

System.Threading.Thread.CurrentThread.IsThreadPoolThread 속성은 현재 쓰레드의 고유 식별자 값을 가져 옵니다

우린 이 속성을 통해 Timer 객체가 수행되는 쓰레드를 알아 보도록 하겠습니다

1. System.Windows.Forms.Timer 의 쓰레드 환경

윈도우 응용프로그램에 최적회 되어 있다는 이 Timer 는 윈도우 응용프로그램 기본 쓰레드와 동일한 쓰레드 상에서 동작합니다

이를 확인하기 위해, 다음과 같이 코드 중간에 IsThreadPoolThread 속성을 확인해 봅니다

- 기본 쓰레드의 고유 번호를 확인한다

윈도우응용프로그램 생성자나 기타 이벤트에서 아래 코드를 기입합니다

MessageBox.Show(System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());

- Timer 쓰레드의 고유번호를 확인한다

Timer 의 Tick 이벤트에서 다음의 코드를 기입합니다

void timer1_Tick(object sender, EventArgs e)
{
MessageBox.Show(System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());

//수행해야할 작업
}

이렇게 확인 해 보면 두 쓰레드는 동일한 고유번호를 반환하게 됩니다

이 말은 곧, 윈도우응용프로가램의 기본 쓰레드인 UI Thread 상에서 Timer이 동작한다는 것을 짐작할 수 있습니다

즉 멀티 쓰레드 환경이 아닌 것이죠

예로, Tick 이벤트에 시간이 긴~ 작업이 수행된다면 프로그램은 그 시간 동안 블럭 된 대기 한다는 것입니다

Timer 의 동작이 기본 프로그램 동작과 독립적으로 수행된다고 생각하시면 안됩니다

2. System.Threading.Timer 의 쓰레드 환경

역시 앞서와 같이 기본 쓰레드와 Timer 쓰레드의 고유번호를 확인 해 봅니다

- 기본 쓰레드의 고유 번호를 확인한다

윈도우응용프로그램 생성자나 기타 이벤트에서 아래 코드를 기입합니다

MessageBox.Show(System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());

- Timer 쓰레드의 고유번호를 확인한다

CallBack 메서드에서 다음과 같이 코드를 기입합니다

void CallBack(Object state)
{
MessageBox.Show(System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());

BeginInvoke(new TimerEventFiredDelegate(Work));
}

어떻습니까? 두 쓰레드는 다른 고유번호를 반환하지요

즉 UI Thread 와 다른 쓰레드, 즉 Work Thread(작업자 쓰레드)임을 알 수 있습니다

이는 곧 멀티 쓰레드가 된 셈이죠. 두 쓰레드는 서로 독립적으로 수행될 것입니다

앞서, System.Windows.Forms.Timer 객체와는 달리 CallBack 메서드에 시간이 오래 걸리는 작업을 수행해도

프로그램이 대기상태로 빠지는 않죠. Timer 동작이 기본 프로그램의 동작과는 독립적으로 수행되는 것이죠.

참고로 이 Timer 는 닷넷의 ThreadPool(쓰레드풀) 에서 관리합니다

3. System.Timers.Timer 의 쓰레드 환경

결론부터 말하자만, 이 Timer 는 기본 쓰레드에서 수행될 수도 있고, 작업자 쓰레드에서 수행될 수도 있습니다

만일, SynchronizingObject 속성을 폼객체로 한다면 Timer는 UI 쓰레드 상에서 동작할 것이며

이 속성을 지정하지 않는다면, 작업자 쓰레드 상에서 동작하게 됩니다

아래와 같이 SynchronizingObject 속성의 설정 여부에 따른 ManagedThreadid 값을 확인해 보기 바랍니다

timer.SynchronizingObject = this;

타이머 쓰레드의 고유번호를 알기 위해 Elapsed 이벤트에

MessageBox.Show(System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());

를 확인해 보세요

---

결국 Timer 의 실행이 기본 쓰레드에서 하느냐, 작업자 쓰레드 에서 하느냐에 차이인데요,

앞서, 사용법의 차이를 살펴 봤을 때 System.Windows.Forms.Timer 객체를 제외하고는 윈도우응용프로그램의 UI 컨트롤

핸들링 시 크로스 도메인 문제가 발생했던 원인이 되는 것입니다

* 기타 차이점 및 요약, 참조

아래 표는 msdn magazine에 소개된 세 Timer 의 차이점에 대한 표입니다

우리가 알아 본 내용 이외에도, 쓰레드 안정성(동기화 문제)에 대한 내용도 있습니다

<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

System.Windows.Forms

System.Timers

System.Threading

Timer event runs on what thread?

UI thread

UI or worker thread

Worker thread

Instances are thread safe?

No

Yes

No

Familiar/intuitive object model?

Yes

Yes

No

Requires Windows Forms?

Yes

No

No

Metronome-quality beat?

No

Yes*

Yes*

Timer event supports state object?

No

No

Yes

Initial timer event can be scheduled?

No

No

Yes

Class supports inheritance?

Yes

Yes

No

* Depending on the availability of system resources (for example, worker threads)

마지막으로 msdn의 설명을 옮기며 마칩니다

서버 타이머, Windows 타이머 및 스레드 타이머

Visual Studio 및 .NET Framework에는 세 개의 타이머 컨트롤 즉, 도구 상자구성 요소 탭에서 볼 수 있는 서버 기반 타이머, 도구 상자Windows Forms 탭에서 볼 수 있는 표준 Windows 기반 타이머 및 프로그래밍 방식으로만 사용할 수 있는 스레드 타이머가 있습니다.

Windows 기반 타이머는 Visual Basic 1.0 이상의 버전에 있으며 지금까지 크게 변경되지 않았습니다.
이 타이머는 Windows Forms 응용 프로그램에서 사용하도록 최적화되어 있습니다.
서버 기반 타이머는 일반 타이머를 서버 환경에서 최적으로 실행되도록 업데이트한 것입니다.

스레드 타이머는 이벤트 대신 콜백 메서드를 사용하는 간단한 소형 타이머로서 스레드 풀 스레드에서 제공합니다.

Win32 아키텍처에는 UI 스레드와 작업자 스레드라는 두 종류의 스레드가 있습니다.
UI 스레드는 대부분의 시간을 유휴 상태로 보내며 메시지 루프에 메시지가 도착할 때까지 기다립니다. 메시지가 도착하면
이 메시지를 처리하고 다음 메시지가 도착할 때까지 기다립니다. 이에 비해 작업자 스레드는 백그라운드 처리를 수행하는 데 사용하며 메시지 루프를 사용하지 않습니다.

Windows 타이머와 서버 기반 타이머는 모두 Interval 속성을 사용하여 실행됩니다.
스레드 타이머의 간격은 <?XML:NAMESPACE PREFIX = MSHelp NS = "http://msdn.microsoft.com/mshelp" />Timer 생성자에서 설정됩니다.
스레드에서 타이머를 다루는 방식을 보면 알 수 있듯이 각 타이머의 용도는 서로 다릅니다.

  • Windows 타이머는 UI 스레드가 프로세싱을 수행하는 데 사용하는 단일 스레드 환경을 위해 설계되었습니다. Windows 타이머의 정확도는 55밀리초로 제한되어 있습니다. 이 일반 타이머는 사용자 코드에서 사용할 수 있는
    UI 메시지 펌프가 필요하며 항상 동일한 스레드에서 실행되거나 다른 스레드로 마샬링됩니다.
    이 기능은 COM 구성 요소의 성능을 저하시킵니다.

  • 서버 기반 타이머다중 스레드 환경에서 작업자 스레드와 함께 사용하도록 설계되었습니다. 두 스레드는 서로 다른 아키텍처를 사용하므로 서버 기반 타이머가 Windows 타이머보다 정확합니다.
    서버 타이머는 스레드 사이를 이동하면서 발생한 이벤트를 처리할 수 있습니다.

  • 스레드 타이머는 메시지가 스레드에서 펌프되지 않는 경우에 유용합니다.
    예를 들어, Windows 기반 타이머는 운영 체제의 타이머 지원 기능에 의존하며 스레드에서 메시지를 펌프하지 않을 경우에는 타이머 관련 이벤트가 발생하지 않습니다. 이 경우에는 스레드 타이머가 보다 더 유용합니다.

Windows 타이머는 System.Windows.Forms 네임스페이스에, 서버 타이머는 System.Timers 네임스페이스에 그리고 스레드 타이머는 System.Threading 네임스페이스에 있습니다.

요즘 트위터 페이스북 더보기

'컴퓨터 > C#' 카테고리의 다른 글

[C#] TCP, UDP MultiThread Server Source Code  (0) 2013.01.07
[C#][Network] RUDP (Reliable UDP)  (0) 2013.01.02
[C#] Timer 세가지와 차이점  (0) 2012.12.31
[C#]현재 콘솔 라인 지우기 함수  (0) 2012.12.28
[C#][네트워크] SharpPcap  (0) 2012.12.05
[C#][네트워크] Pcap  (0) 2012.12.05