SharePoint 2010 Beta 버전은 아쉽게도 SharePoint 2010 RTM 버전으로 마이그레이션을 할 수 없습니다. Microsoft 에서도 이 버전의 Beta 를 RTM 으로 공식적으로 지원하지 않는다고 합니다.

저처럼 SharePoint 2010 Beta 버전이 Go-Live 로 알고 있었던 분이라면 좀 난감하겠네요, Beta 버전을 삭제하는 방법이 아마도 가장 빠른 방법일 겁니다. 하지만 인터넷을 찾아보시면 강제로 Beta to RTM 으로 마이그레이션 하는 방법이 있습니다. http://blogs.breezetraining.com.au/mickb/2010/04/23/UpgradingFromSharePoint2010Beta2ToRTM.aspx

하지만 필자는 위의 방법을 사용하지 않고 기존 SharePoint 2010 Beta 버전을 말끔하게 삭제하는 방법으로 RTM 버전을 설치하고자 합니다.

1. 기존의 SharePoint 2010 RC 버전을 프로그램 추가/제거에서 삭제합니다.

   

2. 레지스트리 편집기(regedit.exe) 를 실행하여, HKLM\Software\Microsoft\Shared Tools\Web Server Extensions 키를 삭제합니다.

   

3. C:\Program Files\Microsoft Office Servers 폴더를 삭제합니다.

   

4. C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions 폴더를 삭제합니다.

   

5. SQL Server 의 다음의 데이터베이스를 삭제합니다.

   

6. 모든 과정을 정상적으로 삭제 되었으면, SharePoint 2010 RTM 버전을 설치합니다.

발생 문제

Team Foundation Server 2010 을 재설치 한 후에, 로컬에서 소스 코드를 바인딩 하기 위해 작업 영역(Workspace) 를 매핑하고자 합니다.

하지만 아래와 같은 메시지로 작업 영역과 로컬 폴더간의 매핑이 이루어 지지 않습니다.

   

   

발생 원인

이 문제는 Team Foundation Server 의 정보의 일부를 로컬에 Cached 하고 있습니다. 로컬 컴퓨터에 Cached 가 되는 이유는 서버가 접속할 수 없는 경우 또는 오프라인일 경우에도 매핑 정보를 유지하기 위함입니다.    

매핑 파일의 정보는 아래의 경로에서 찾을 수 있습니다.
C:\Users\사용자 폴더\AppData\Local\Microsoft\Team Foundation\3.0\Cache

   

해결 방법 

이 문제를 해결 하기 위해 로컬에 Cached 된 정보를 삭제합니다. 아래의 경로로 이동합니다.
C:\Users\umc\AppData\Local\Microsoft\Team Foundation\3.0\Cache

   

이동한 폴더의 VersionControl.config 파일을 아래와 같이 삭제합니다.

또는

VersionControl.config 파일의 repositoryGuid 등을 현재의 Guid 로 변경해 주시거나, 매핑하고자 하는 TFS 서버의 정보만 삭제한다면 기존의 매핑 정보를 그대로 사용할 수 있습니다.

또는 아래와 같이 문제가 발생되는 선택 영역의 부분의 XML Element 만 삭제하시면 됩니다.

위와 같은 방법으로 해결 된 경우, Visual Studio 를 재시작하지 않고 바로 적용이 가능합니다.

지난 6월 1일, 서울 삼성동 코엑스 인터컨티넨탈 호텔에서 호화스럽게 열린 REMIX10 행사가 있었습니다. 이날 행사에서는 천 여명 훌쩍 넘는 분들이 참석하셨습니다.    

많은 분들이 평일에 개최된 행사라 사정이 여의치 않은 분들께서는 참석하지 못하신 것으로 알고 있는데, Microsoft Korea 에서 이날 모든 세션을 녹화하여 드디어 REMIX10 행사의 모든 세션이 공개가 되었습니다.    

비록 이 날 참석하지 못하신 분들도, 아래의 링크를 통해 녹화된 비디오를 시청하실 수 있습니다. http://www.visitmix.co.kr/remix10/agenda.html

특히, 이 날 키노트 세션은 모든 세션의 중요 정보를 한 눈에 볼 수 있는 중요한 시간이기도 합니다. 모든 세션을 시청하기 시간이 부족하신 분들이라면, 키노트 세션은 꼭 한번 보시라고 권장합니다.
http://www.techdays.co.kr/2010spring/remix10/keynote.html    

[MFC/윈도우 7 멀티터치] #3 : 제스처(gesture)를 이용한 구현(下)

MFC 2010. 6. 16. 09:00 Posted by 알 수 없는 사용자

Intro

안녕하세요~ MFC 카테고리의 꽃집총각 입니다.

이번 포스팅에서는 앞 글에서 이야기 중이었던 예제 프로그램의 나머지 부분을 마저 작성하고, MFC 기반의 프로젝트에서 제스처를 이용해 멀티터치 프로그래밍을 제어하는 방법에 대한 보충 설명을 정리해 보도록 하겠습니다. 앞에 적은 글과 이어지는 내용이니까, 앞부분을 보지 못하신 분들은 ( http://vsts2010.net/292 ) 이 글을 먼저 읽어주세요 ^^ 지난번 글에서는 멀티터치 동작을 확인하기 위한 Drawing 처리까지 함께 알아봤습니다. 앞부분에서 진행됐던 내용을 소제목만 간추려서 알아보면,

* 제스처를 인식하는 첫 번째 예제 프로그램 *

  1. MFC Application 프로젝트를 만들자
  2. 하드웨어 상태를 알아봅시다
  3. View 영역에 사각형을 그리자!

…와 같은 내용이었습니다. 두 번째 스텝에서 알아봤던 하드웨어의 멀티터치 인식 가능여부 확인방법을 제외하고는 멀티터치와 직접적으로 연관된 내용은 많이 없었군요. 이번 시간부터는 본격적으로 터치 입력을 제어하는 코드를 함께 알아보겠습니다.

 

Task 4 : 터치 인식 코드를 넣을 차례!

자, 이제 드디어 제스처를 제어하는 코드를 알아볼 시간입니다. 사용자 경험(UX;User Experience) 분야에서 가장 뜨거운 이슈인 멀티터치! 뭔가 색다르고 놀라운 구현방식이 있을 거 같지만 실망(?)스럽게도 구현방법은 기존의 MFC 프로그래밍과 별다른 바가 없습니다. 그저 적당한 함수를 오버라이딩 해서 필요한 코드를 넣어주면 그만이거든요. 바로 이 점이 새롭게 변화된 MFC 추가기능들이 마음에 드는 이유 중에 하나입니다. 바로 ‘친숙한 인터페이스’ 말입니다 :)

MFC의 윈도우 랩핑 클래스인 CWnd에는 이번에 새롭게 추가된 제스처 관련 함수들이 있습니다. 이 중에서 아래의 다섯 가지 제스처 핸들링 함수를 오버라이딩 합니다. 각각의 함수들은 이름에 명시되어 있는 제스처가 입력됐을 때 호출됩니다. 아래의 코드를 ChildView.h 파일의 class CChildView 선언에 넣어줍니다.

// Overrides
protected:
    // Gesture handlers
    virtual BOOL OnGestureZoom(CPoint ptCenter, long lDelta);
    virtual BOOL OnGesturePan(CPoint ptFrom, CPoint ptTo);
    virtual BOOL OnGestureRotate(CPoint ptCenter, double dblAngle);
    virtual BOOL OnGesturePressAndTap(CPoint ptFirstFinger, long lDelta);
    virtual BOOL OnGestureTwoFingerTap(CPoint ptCenter);

기본적으로 터치가 인식되면 애플리케이션으로 WM_GESTURE 메세지가 날아옵니다. Win32 API만을 이용해 제스처를 제어하려면 WndProc 프로시저 함수에서 WM_GESTURE를 잡아서 처리해야 하지만, MFC 애플리케이션의 경우 자체적으로 각각의 제스처에 대한 전용 핸들링 함수들이 나뉘어져 있기 때문에, 제어가 필요한 제스처에 해당하는 함수를 용도에 맞게 따로따로 오버라이딩 해서 사용하면 됩니다.

Note : 그런데 CWnd의 선언을 살펴보면 (afxwin.h 파일에 있습니다.) 위에 나열된 함수 말고도 OnGesture 함수가 정의되어 있는 것을 확인하실 수 있습니다. 함수의 원형은 아래와 같습니다.

afx_msg LRESULT OnGesture(WPARAM wParam, LPARAM lParam);

오호라~ 이것은 WM_GESTURE 메세지가 넘어올 때마다 해당 메세지를 직접 제어할 수 있는 함수인가 보군요~ 함수의 인자로 wParam, lParam이 모두 날아오니 win32 프로젝트에서 하는 것처럼 코딩하고 싶을 땐 이걸 상속받아서 작업하면 되겠구나 ~ … 라고 생각하고, 실제로 테스트 코드도 만들어 봤었지만 안되더군요 @.@… 똑똑한 여러분들은 이유가 무엇인지 바로 찾으셨을 거라고 생각합니다만… 저 함수는 가상 함수가 아닙니다 ^^;… 그저 이름만 보고 상속받아 써야지 했는데, 나중에 보니 재정의 할 수 없는 일반 멤버함수더라고요 ㅎㅎ 아마도 CWnd가 자체적으로 처리하는 코드를 구현한 부분이 아닌가 생각됩니다. 일반적으로는 각각의 제스처마다 독립적으로 호출되는 위의 다섯 가지 함수를 이용하면 되고요, 경우에 따라 부득이하게 WM_GESTURE 메세지를 직접 제어하고 싶을 때엔… 이전에 그랬던 것처럼 WndProc을 직접 제어하도록 하고 그곳에서 WM_GESTURE를 받은 경우에 대한 switch-case 문을 넣어주면 되겠죠 ^^

이제 위에 소개된 다섯 개의 함수들에서 view 영역에 그려지고 있는 사각형을 제어하는 코드들을 넣어줍니다. 아래에 함수들의 본문 코드가 있습니다.

BOOL CChildView::OnGesturePan(CPoint ptFrom, CPoint ptTo)
{
    int dx = ptTo.x - ptFrom.x;
    int dy = ptTo.y - ptFrom.y;
    
    if (dx != 0 || dy != 0)
    {
        m_drawingObject.Move(dx, dy);
        RedrawWindow();
    }
    return TRUE;
}
 
BOOL CChildView::OnGestureZoom(CPoint ptCenter, long lDelta)
{
    if ((m_pCurrentGestureInfo->dwFlags & GF_BEGIN) == GF_BEGIN)
    {
        m_dblZoomRatioStart = m_dblZoomRatioTotal = lDelta;
    }
    else if (lDelta != 0)
    {
        m_dblZoomRatioTotal += lDelta;
        double zoomFactor = (double)m_dblZoomRatioTotal / m_dblZoomRatioStart;
        
        m_drawingObject.Zoom(zoomFactor, ptCenter.x, ptCenter.y);
        
        m_dblZoomRatioStart = m_dblZoomRatioTotal;
        RedrawWindow();
    }
    return TRUE;
}
 
BOOL CChildView::OnGestureRotate(CPoint ptCenter, double dblAngle)
{
    if ((m_pCurrentGestureInfo->dwFlags & GF_BEGIN) == GF_BEGIN)
    {
        // Make the first center, the rotating one
        m_ptCenter = ptCenter;
    }
    else if (dblAngle != 0.)
    {
        m_drawingObject.Rotate(dblAngle * PI / 100.0, m_ptCenter.x, m_ptCenter.y);
        RedrawWindow();
    }
    
    return TRUE;
}
 
BOOL CChildView::OnGesturePressAndTap(CPoint ptFirstFinger, long lDelta)
{
    if ((m_pCurrentGestureInfo->dwFlags & GF_BEGIN) != 0)
    {
        m_drawingObject.ShiftColor();
        RedrawWindow();
    }
    
    return TRUE;
}
 
BOOL CChildView::OnGestureTwoFingerTap(CPoint ptCenter)
{
    m_drawingObject.TogleDrawDiagonals();
    RedrawWindow();
    
    return TRUE;
}
 

함수의 이름과 전달되는 인자들 모두 직관적입니다. 함수 본문의 예제 코드들도 그리 어렵지 않군요. 위의 코드들을 복사해 적어준 뒤 빌드하고 프로그램을 실행해 봅니다. 애플리케이션이 뜨면 사각형을 손으로 움직여 보세요. 터치 입력에 따라 사각형은 이동하고, 늘어나고, 회전할겁니다 :)

네…? 근데 뭔가가 안 된다고요?

 

Task 5 : 다 되는 거 같은데 회전만 안되네요 ㅡㅜ…

여기까지 진행하고 테스트를 해보면 다른 제스처는 다 인식을 하는데, 회전(rotate)만 제대로 안 되는 현상을 겪으실 겁니다. 참고로 이번 예제에서 확인하실 수 있는 다섯 가지 제스처에 대한 동작을 간략히 설명 드리면 아래와 같습니다.

panning 손가락을 스크린에 대고 이동한다.
zoom 두 손가락을 스크린에 대고 벌리거나 모은다.
rotate 두 손가락을 스크린에 대고 회전시킨다.
press and tab 한 손가락을 스크린에 댄 상태에서 다른 손가락으로 스크린을 빠르게 터치한다.
two finger tab 두 손가락을 동시에 스크린에 붙였다 뗀다.

그런데 다른 건 다 잘되는데 아마 rotate 제스처만 동작하지 않을 거예요. 그리고 다른 제스처들은 다 인식할 테지만… 사각형 내부에서 일어난 제스처인지를 판단하는 처리가 없었기 때문에, 꼭 사각형 내부가 아니더라도 client 영역 내에서 발생한 유효한 제스처라면 모두 다 인식하는 걸 확인할 수 있으실 겁니다.

그 이유는 코드가 잘못된 것이 아니라, 윈도우 자체에서 다른 제스처들은 모두 기본적으로 활성화 되어있는데, rotate만은 기본적으로 비활성화 되어있기 때문입니다. 활성화 하려면 다 해두든가 아님 말든가 할 것이지 왜 rotate만 천대(?)하는지 자세한 내막은 모르겠습니다만… 그것이 현실이군요. rotate를 활성화하기 위해, 제스처의 설정(config)를 제어하는 방법을 알아보도록 합시다.

MFC에서는 제스처의 설정을 쉽게 컨트롤 할 수 있도록 CGestureConfig라는 클래스를 제공합니다. CChildView의 선언( in ChildView.h )에 CGestureConfig 타입의 멤버변수를 하나 추가해줍니다.

class CChildView 
{
    // ...
 
// Fields
protected:
    // Holds gesture configuration
    CGestureConfig m_gestureConfig;
    
    // ...
}

그리고 새롭게 부활한 반가운 인터페이스, 마법사 중의 마법사 MFC Class Wizard를 띄워서 OnCreate 함수를 오버라이딩 합니다. (단축키 Ctrl + Shift + X 입니다 ^^)

그리고 아래의 코드를 넣어주세요.

 
int CChildView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CWnd::OnCreate(lpCreateStruct) == -1)
        return -1;
 
    // TODO: Add your specialized creation code here
    GetGestureConfig(&m_gestureConfig);
    
    // Only rotation is not enabled by default
    m_gestureConfig.EnableRotate();
    
    SetGestureConfig(&m_gestureConfig);
 
    return 0;
}
 
 
 
그리고 나서 다시 한번 빌드하고 실행해 봅니다. 이제 rotate 제스처도 제대로 인식하는 첫 번째 예제 프로그램이 완성 되었습니다 ^^*
 
자 드디어 첫 번째 예제의 완성입니다~ !! 짝짝짝 ~
 

Task 6 : panning이 왜 이렇게 뻑뻑하지? 아이폰은 안 그런뎅..

 
task5 까지가 channel9에서 소개한 MFC gesture 기능의 첫 번째 예제입니다. 필요한 부분과 불필요한 부분을 적절하게 구분해 잘 설명해 두어서, 한번 설명을 따라 작성해 보시는 것만으로 제스처 프로그래밍에 대한 대략적인 흐름을 손쉽게 파악하셨을 거라고 생각합니다.
 
이대로 끝내기엔 좀 아쉬운 감이 있어서… 예제 코드에 대한 추가 설명을 드리고자 합니다. 사실 글 중간중간에도 제가 추가로 적은 글이 제법 있었지만… 마땅히 말씀드릴 타이밍을 못 찾았던 요것 한 가지만 더 말씀 드리도록 할게요 ㅎㅎ
 
일단 예제를 조작해 보면 제일 먼저 거슬리는 부분이 바로 panning 제스처에 대한 조작감(?) – 게임개발만 오래 하다 보니 이런 표현이…^^; – 인데요. 아이폰에서 느껴지는 부드러운 반응성에 비해 사각형 움직임이 무척이나 둔하고 불만족스럽다는 걸 느끼실 겁니다. 사각형이 한 번 움직이고 나면 괜찮은데, panning 제스처를 인식해 움직여지기 시작할 때 까지가 뭔가 걸리는 느낌입니다. x, y 한 방향으로만 움직이고, 다른 방향으로는 안 움직이는 경우도 아마 겪으셨을 거예요. 왜 이럴까요? 기기의 한계? 아님 OS 자체의 한계?
 
이런 현상은 하드웨어의 문제나 OS 기능 자체의 문제가 아닙니다. 이것도 역시 panning 제스처의 옵션에서 GC_PAN_WITH_GUTTER 플래그가 기본적으로 켜져 있기 때문에 나타나는 현상입니다. 저도 처음에 제스처(gesture)를 이용한 멀티터치 예제들을 실행해 보다가 panning의 무딘 반응성이 너무 눈에 거슬려서, 부드러운 움직임을 보려면 결국 WM_GESTURE에서는 한계가 있고, WM_TOUCH를 써야 하는가 보다 했었는데, 간단히 플래그만 조절해주면 좀 더 부드러운 움직임을 느낄 수가 있게 됩니다.

GC_PAN_WITH_GUTTER에서 gutter는 홈통이나 배수로 등의 뜻으로, panning 제스처가 발생했을 때 가장 메인이 되는 방향으로의 움직임 이외의 방향성에 대한 반응은 특정 임계값을 넘기지 않는 이상 무시하게 하는 효과를 줍니다. 이 때문에 손가락에서의 미세한 움직임들이 무시되고, 반응성이 안좋다는 느낌을 받게 되죠. 이를 해결하기 위해서는 rotate 제스처의 활성화를 위해 오버라이딩 했었던 OnCreate 함수에서 panning에 대한 아래의 설정도 함께 처리해 주면 됩니다.

 
config.EnablePan( TRUE, 
    GC_PAN_WITH_SINGLE_FINGER_VERTICALLY | 
    GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY |
    GC_PAN_WITH_INERTIA );
 
pan 제스처를 활성화 하되, GC_PAN_WITH_GUTTER 플래그만을 제외한 나머지 플래그들을 활성화 해주는 코드입니다. 위 코드를 넣고 나서 빌드하고 다시 실행해보면 이전보다 훨씬 더 부드럽게 사각형이 이동되는 것을 느낄 수 있으실 겁니다.

 
 

Outro

이것으로 MFC를 이용해서 제스처를 이용한 멀티터치 프로그래밍 방법의 첫 번째 예제를 소개와 설명을 모두 마쳤습니다. 제스처의 설정을 컨트롤 하는 방법, 각각의 제스처에 대한 핸들링 코드를 넣어주는 방법 등을 알아보았는데, 대부분 예전 MFC 프로그래밍의 마우스/키보드 이벤트 처리 등과 비슷한 방식이었기 때문에 그리 어렵지는 않은 난이도였습니다.

다음 포스팅 에서는 이제 가장 자유롭고 확장성 있는 (… 하지만 대신 좀 더 까다로운 ) WM_TOUCH를 이용한 멀티터치 프로그래밍 방법을 알아 보도록 하겠습니다. 혹시 그 전에 질문 사항들이나, 제스처 프로그래밍 방법들에 대한 추가 학습사항이 있다면 다시 한 번 정리하는 기회를 갖도록 하겠습니다.

그럼 다음 포스팅에서 다시 인사 드리도록 하겠습니다. 그 때까지 더운 날씨에 모두들 건강하시고 공부 열심히 하세요 ~ ^^*

감사합니다 ~ 꾸벅 ~

Reference

[JumpToDX11-13] Tessellation 등장.

DirectX 11 2010. 6. 15. 09:00 Posted by 알 수 없는 사용자


요즘 vs2010 에 아티클이 많아져서 너무 좋습니다..^^


< Tessellation 개념 잡기 >

지금부터 언급할 내용은 Tessellation 입니다.
Tessellation을 간단히 정의하자면, 적은 수의 폴리곤이 그래픽 파이프라인을 통과했을 때,
많은 수의 폴리곤들을 생성해서 최종 화면에서는 훨씬 정밀한 결과를 나타내는 기술
이라고
할 수 있습니다.
간단히 이미지를 보시면 쉽게 개념화 할 수 있을 것입니다.






감동적이신가요?
저는 꽤 큰 감동을 받기는 했는데, 어마어마한 연산량이 걱정이 되었습니다.
물론 여러 분들도 저와 같은 생각일 것이라고 생각합니다.



< Tessellation 의 등장 배경 >

오늘 날의 컴퓨터 그래픽스의 발전은 정말이지 급격하게 변화했습니다.



유명한 파이날 판타지7 과 최신작을 비교해 보았습니다.
그래픽적인 큰 변화가 느껴지시나요?
아마도 느껴지실 것입니다.( 안느껴지시면 곤란합니다..^^ )

저 변화의 중심에 서 있는 기법 혹은 기술은 어떤 것일까요?
다양한 의견이 있을 수 있지만, 개인적인 견해를 전제로 제가 언급하면 텍스쳐링이라고 생각합니다.
오늘 날의 하나의 폴리곤에 적용되는 텍스쳐의 갯수는 하나가 아닙니다.
노말맵이 거의 표준적으로 사용되고 있는 현 세대에서는
각종 라이팅 처리를 위해서 많은 갯수의 텍스쳐가 사용되고 있습니다.
그래서 우리는 현실감 있는 게임을 즐길 수 있습니다.
이러한 발전의 방향은 폴리곤 갯수를 증가시키는 것보다,
텍스쳐링을 활용하는 것이 성능적인 측면에서 더욱 효과적이기 때문입니다.

그러던 과정에서 이제는 GPU의 성능이 급격히 발전하기 시작했습니다.
많은 사람들이 GPU의 활용에 대해서 고민하기 시작했고,
DirectX9 부터 이런 GPU을 활용한 Tessellation 위한 기법들이 공개적으로 소개되기 시작했습니다.
특히나 ATI 쪽에서는 DirectX9 을 위한 Tessllation SDK 를 제공했었습니다.
여담이지만, 엔비디아쪽에서는 자사의 GPGPU 인 CUDA 를 DirectX9 에서 지원했었습니다.
두 회사의 발전 방향이 이때부터 사실 조금씩 차이가 나기 시작했었다고 볼 수 있습니다.



위의 그림은 ATI 사에서 Tessellation의 필요성을 표현하고 있는 그림입니다.
텍스쳐링을 아무리 많이해도, 폴리곤 갯수가 적으면 더 큰 현실감을 느끼는데는 제한이 있다는 정도로 정리할 수 있을 것입니다.
( 그림(c) 에서 몬스터의 부자연스러운 손가락이 보이시죠? )

그래서 조금 더 큰 현실감을 위해서 폴리곤을 증가시키는 방법을 고안하게 되었고,
급기야 이것이 현 DirectX11 의 정식 그래픽 파이프라인 스테이지로 추가되었습니다.
즉, 공부할 것이 훨씬 더 많아졌습니다...T.T


< 왜 Tessellation 인가? >

조금 과장된 표현을 해서, 게임에서 폴리곤을 많이 사용하는 것은 범죄(?) 행위에 해당합니다.
그래픽 카드가 놀라운 속도로 발전을 하고 있지만,
아직도 게임 개발자들은 비디오 메모리의 부족을 호소하고 있습니다.
당연한 얘기지만, 이는 폴리곤 갯수와 퀄리티의 증가에 의한 것입니다.



위의 그림처럼 그래픽 카드는 약간 독특한 성능을 가지고 있습니다.

첫번째로 대역폭입니다.
CPU쪽 대역폭보다 훨씬 크기 때문에, 대량의 데이터를 전송할 수 있습니다.

두번째는 비디오 메모리가 시스템 메모리 보다 훨씬 작다는 것입니다.

세번째는 수치 연산과 병렬연산에 강한 GPU 라는 것입니다.
실제로 Tessellation 파이프라인 스테이지는 병렬적으로 처리됩니다.
( 다음 시간에 이에 대한 언급이 있을 것입니다. )

결과적으로 Tessellation 의 이점은
폴리곤 갯수를 줄임으로써 비디오 메모리 사용량을 감소시킵니다.
이는 결국 적은 데이터 전송으로 인해 대역폭을 절약할 수 있습니다.
하지만, Tessellation 은 GPU 의 성능에 좌우된다고 할 수 있습니다.
연산량이 실제로 많기 때문에, 정말이지 빠른 성능이어야 한다는 것입니다.
다행스러운 것은 GPU 의 성능이 비디오 메모리의 확장보다는 더 빨라지고 있다는 것입니다.

사실 Tessellation 에 대한 가장 큰 의구심은 '과연 빠를까?' 입니다.
이것에 대한 정답은 아직은 없습니다.
적절한 곳에서 사용한다면 유용할 수도 있을 것이고, 그렇지 않을 수도 있을 것입니다.
다만, 현재 DirectX 의 새로운 패러다임으로 Tessellation 이 선택되어졌으며,
좋은 성능을 위해서 꾸준히 노력할 것이라는 것입니다.^^

Hello Windows Azure / Windows Azure Tools for Visual Studio 1.2 출시

Cloud 2010. 6. 14. 19:00 Posted by 알 수 없는 사용자

안녕하세요. Visual C# MVP 남정현입니다. 본 강좌를 올리기 전에 일종의 공지 사항 겸 소개를 위하여 이 글을 먼저 올리게 되었습니다. 얼마전에 Windows Azure Tools for Visual Studio 1.2 (2010년 6월 업데이트)가 새롭게 출시되었습니다. 메이저 버전도 아니고 마이너 버전인데 이와 같이 별도의 글을 써서 올리는 이유는, 다름이 아니라 Windows Azure Tools for Visual Studio의 기능에 많은 변화가 있었기 때문입니다.

Windows Azure SDK 1.2에서는 최근 발표된 .NET Framework 4.0 런타임을 내장한 Windows Azure OS 2010년 4월 버전에 대한 요구 사항을 모두 반영하고 있습니다. .NET Framework 4.0 기반의 응용프로그램을 작성 중이신 분들도 이제는 Windows Azure 환경 위에서 직접 응용프로그램을 호스팅할 수 있습니다. 그리고, Visual Studio 2010에서 새로 추가된 향상된 디버깅 기법인 IntelliTrace를 직접 지원하기 때문에 이전보다 더욱 쉬운 디버깅이 가능합니다.

다음은 Windows Azure Tools for Visual Studio 1.2의 변경 사항들입니다.

  • Visual Studio 2010 RTM 지원: Visual Studio 2010 RTM 버전을 지원합니다.
  • .NET Framework 4.0 지원: 이제는 .NET Framework 3.5와 4.0을 빌드 타겟으로 동시에 지정할 수 있습니다. 단, 4.0을 빌드 타겟으로 지정하게 되는 경우, Windows Azure OS의 이미지 중 2010년 초반의 일부 버전은 사용할 수 없습니다.
  • Cloud storage explorer 추가: 읽기 전용 기능이 지원되는 Windows Azure Table 및 BLOB 컨테이너 보기 프로그램이 서버 탐색기 패널을 통하여 제공됩니다. SQL Azure를 데이터베이스 목록에 추가하고 동시에 관리할 수 있어 편리합니다.
  • 통합 배포 환경: 솔루션 탐색기에서 Windows Azure 프로젝트를 오른쪽 버튼으로 클릭하여 'Publish' 메뉴를 클릭하면 이제는 직접 배포가 가능합니다. 이전 1.0 및 1.1 버전의 경우, CSPKG 파일과 CSCFG 파일이 생성된 디렉터리의 폴더가 Windows 탐색기에서 열리고, Windows Azure Portal Web Site가 웹 브라우저로 열렸기 때문에, 브라우저에서 로그인하고 여러 단계에 걸쳐서 배포할 파일을 찾아가야했기 때문에 Deploy 절차가 복잡했습니다. 통합 배포 환경에 대해서는 본 강좌의 말미에 한 번 다룰 수 있도록 하겠습니다.
  • 서비스 모니터링: 서비스의 상태를 웹 제어판이 아닌 "서버 탐색기"의 compute 노드에서 실시간으로 모니터링할 수 있습니다.
  • IntelliTrace 기능 지원: Visual Studio 2010을 사용하는 경우, IntelliTrace 기능을 Windows Azure 서비스에 배포한 이후에 "직접" 사용할 수 있습니다. 이 기능은 앞에서 설명한 통합 배포 환경의 일부로 동작하며, 배포가 끝난 이후에 자동으로 이 기능이 시작되어 서비스 모니터링과 함께 실시간으로 로그가 모니터링됩니다.

그간 Windows Azure 기반의 개발이 어려우셨다면 이제는 최신 버전의 SDK와 개발 도구를 이용하여 좀 더 편안하게 Visual Studio 2010을 통해 클라우드 기반 응용프로그램 개발을 시작해보세요. 이러한 기능들은 모두 Windows Azure가 제공하는 Management REST API를 기반으로 작성된 것이며, REST API의 Specification들은 모두 문서화되어있으므로, 누구나 쉽게 응용할 수 있습니다.

참고로 Visual Studio 2010의 데이터베이스 탐색기는 SQL Azure 데이터베이스에 대한 탐색도 지원하므로 이전처럼 별도의 SSMSE를 필요로 하지 않습니다. 그리고 이번 버전의 SDK 역시 1.0과 1.1과 마찬 가지로 처음 설치하는 경우에는 OS와 개발 도구의 환경에 따라 패치와 핫 픽스들을 적용하셔야 합니다.

참고로, IntelliTrace 기능은 Visual Studio 2010 Ultimate에서만 사용이 가능합니다. Visual Web Developer 2010 Express에서는 기본적인 Deploy 기능만이 지원됩니다.

다운로드 바로가기

.Net Ria Service + IIS6 + Silverlight 4 Troubleshooting!!

RIA 2010. 6. 14. 11:31 Posted by 알 수 없는 사용자



기쁜 마음으로 아주 오래간만에 포스팅을 합니다.
문제는 간단한 거였는데 상당히 돌아온 느낌이 드는군요.
하지만 그 와중에 많은 글들을 보았고 또 많은 것들을 배웠습니다.

힘들었던 하루지만 돌이켜 보면 재밌네요. :)

문제)

Windows 7 x64, IIS7 에서 개발을 했고 Windows 2003 Server R2, IIS6 에 배포하려했습니다.
Visual Studio 2010 Ultimate, MS-SQL 2005

실버라이트 4 와 .Net Ria Service (이하 리아서비스)를 이용하여 프로젝트를 만들었습니다.
개발 할 때는 전혀 문제가 없었는데 막상 배포하려고 하면서 문제가 발생하기 시작했습니다.

배포 하면서 몇가지 의문이 생겼습니다.
1. 리아서비스가 IIS 6에서도 돌아갈까..
 - WCF Ria Service 는 IIS6, IIS7을 지원한다고 합니다. 문제 없이 돌아가죠.

2. .NET Framework 4가 필요한가?
 - 프레임 워크 4가 필요하더군요.
 어떤 글에서는 3.0도 돌아간다는 글을 본것 같은데 문제는 제가 Linq 쿼리를 상당히 많이 사용했고, 
 또 실버라이트 4 비지니스 어플리케이션으로 개발하다 보니 프레임워크 4가 필요했습니다.

...

서버에 프레임워크 4를 설치했습니다.
WCF Ria Service를 설치 하기에 앞서 Silverlight SDK 를 먼저 설치했습니다.
파일 이름은 "RiaServices.msi" 군요.. 이놈이 요구하는게 Silverlight SDK 였습니다.

배포 환경이 만들어 졌겠다 싶어서 얼른 실버라이트 웹 프로젝트에서 필요한것만 추려냈습니다.


Bin - 꼭있어야겠죠.. 빌드 본이니.
ClientBin - Silverlight xap(잽) 파일이 존재하는곳이죠.
*.js - 전 어차피 javascript를 안쓰기때문에 필요없지만 혹시나 필요하게 될까봐. 그냥 두었습니다.
web.config - 문제의 주범이였습니다.

나머지 두개의 .xml 파일은 WCF  서비스를 할때에 필요했던거라 포함시켜봤는데 테스트는 안해봤습니다.
이 두놈이 도메인서비스에서도 필요할지는 잘 모르겠네요.

IIS 에 잘 모셔두고 MIME Type과 권한 설정, Asp.net Framework 변경을 마치고 테스트를 했습니다.
뚜둥..~

Load Error
System.ServiceModel.DomainServices.Client.DomainOperationException : Load operation failed for query 
'GetTbl_Curriculum'. Remote server returned an error ---> 







해결)

처음 보는 에러라서 적잖게 당황했습니다.
뭘 잘못했지..
팀아저씨의 블로그에서 좋은 글을 발견했습니다.

Deploying your Silverlight and WCF RIA Services application

영어라서 압박이 있었는데 처음보는 어셈블리가 보였습니다.
System.Web.Ria 이분 수소문 해봤습니다.
알고보니 Visual Studio 2010 RC 시절에 잠깐 등장했던 분으로 이름이 바꼈네요.


에서 System.Web.Ria 가 아니라. 아래 세분을 위와 같이 해주시면 되겠습니다.


덤으로 이분도 추가해주시길.. System.ComponentModel.DataAnnotations.dll
Copy Local = True 로 하니 빌드시 Bin 폴더로 쏙 들어가네요.
팀 아저씨 글중에 "msiexec /i RiaService.msi SERVER=TRUE"라는게 있는데 보아하니 Ria Service를 설치하는거 같은데요.. 아직까지 뭔지 모르겠습니다.
해봤는데 안되고 해서 패스했습니다.
사실 아까 Silverlight SDK 설치 후에 RiaServices.msi 파일 설치했으니깐요.. 뭐 같은거라 봅니다만..

팀아저씨 글은 어려웠습니다. 뭐 저리 수정해야될게 많은가.. 보아하니 비슷하기도 하고 예전꺼라서 틀린것도 있고해서
web.config 파일은 수정하지 않았습니다.

뭐 이제 리아서비스 관련 dll도 추가했으니 되었겠다. 싶어서 테스트를 해봤습니다.

Load Error
System.ServiceModel.DomainServices.Client.DomainOperationException : Load operation failed for query 
'GetTbl_Curriculum'. Remote server returned an error ---> ..


사실 이 에러는 생각해보니 오늘 하루 백번은 본것 같습니다.

 WCF 서비스를 만들때 우리들은 Myservice.svc ".svc" 확장자를 가진분을 보았을 겁니다.
리아서비스는 왜 이분이 안보일까요.!!

에러내용도 도메인 서비스에서 쿼리를 못가져오겠다고 하니깐.. 이분한테 문제가 있을것 같았습니다.
저의 예상은 그대로 적중!

다들 아시는 내용인가요..저만 몰랐던 건가요.
도메인 서비스의 .svc 파일은 런타임때 생성이 된다고 합니다.
이분을 찾아야했습니다.

분명히 엔드포인트가 있을것이야!!!

그분의 엔드포인트는 이런식으로 - -; 찾아갈 수 있었습니다.
1. SolKongpill - 제 솔루션 이름
2. localhost:1234 - 제 도메인 이름
3. kongpillDomainService - 제 도메인 서비스 이름.
 - 비지니스 프로젝트를 만들어보시면 알겠지만 도메인 서비스는 주로 웹 프로젝트 하위의 Services 폴더 에 만듭니다.
 - 꼭 그러실 필요는 없으나 알아두세요.

이분을 조합해서 만듭니다.

http://localhost:1234/SolKongpill-Web-Services-kongpillDomainServices.svc

조잡한가요.. 아니면 슬러쉬를 그냥 '-' 로 대채한 느낌일까요..
아무튼 이게 답니다. 이분을 브라우저 주소에 옮겨보니..


인증구성이 어쩌구 합니다.

이분께서 잘못되었으니 쿼리를 찾을 수없다 뭐시다 예외가 나왔던 겁니다.

IIS / 제가 만든 웹서비스 / 속성 / 인증 / 편집 에 가봤습니다.
"익명 엑세스 허용", "윈도우 통합 인증" 두개가 체크 되어있네요.

"익명 엑세스 허용"만 하고 "통합 인증"은 체크 해제 합니다.
web.config 파일도 수정해주셔야합니다.

   ====>



 
윈도우 인증으로 바꿔주셔야합니다.

"IIS 6 재시작"


반가운 화면입니다. 
실제로 실버라이트 페이지도 문제 없이 돌아가네요.
문제의 원인이 무엇이고 그 문제에 대한 예외 메세지를 보니 의외로 쉽게 처리할 수 있었던 문제였네요.

참고로 IIS 의 특정 폴더 하위에 개발하신 프로젝트를 두셔야 될 경우는 꼭 
가상웹사이트를 만들어서 사용하시길 바랍니다. 그냥 폴더 하위로 링크를 걸어두니깐 오류가 나더군요.

Welcome to Parallel C#(7) - Excuse me.

C# Parallel Programming 2010. 6. 14. 09:00 Posted by 알 수 없는 사용자

- 뭐, 미안하다고?

선진국에 가보면, 약간만 부딛힐 듯한 상황이라면, 서로 'Excuse me', '스미마셍'같이 서로를 배려하는 모습을 볼 수 있습니다. 우리나라에서는 아직 길을 걸으면서 뒷 사람에게 담배연기를 선사한다던가, 뭐 그리 급한지 보행자일 때는 운전자를, 운전할 때는 보행자를 씹으면서 급하게 서두르는 모습을 쉽게 볼 수 있습니다. 층간소음이 일어나면 오히려 윗집이 더 큰소리를 치기도 하죠. 시민의식으로 겨루는 한일전에서도 완승을 거뒀으면 좋겠다는 생각을 합니다만, 저 역시 모범시민은 아니기에 같이 노력해야겠죠. 어허허허헣. 오늘은 닷넷이 예절을 얼마나 잘 지키는 지, 한번 살펴보겠습니다.


- Stop it, Now!

위 소제목을 보시고, 잭 바우어를 떠올렸다면, 24시의 팬이시겠군요. 잭 바우어는 너무나도 급한 상황을 많이 만나는데요, 상대방에게는 정말 미안하지만, 상황을 해결하기 위해서 윽박지르고, 때로는 때리고, 아주 때로는 다리를 쏘는 등등등! 의 방법을 사용합니다.

아흙. 닷넷의 멀티 스레드 환경을 한번 생각해보죠. 여러개의 스레드가 작업을 처리하는 동안, 하나의 스레드는 사용자의 UI에서 입력을 기다립니다. 그리고 사용자가 취소버튼을 누르면, 사용자의 의지를 이어받아서 다른 스레드들을 취소해야 하는데요. 기존의 .NET 3.5까지는 작업 중인 스레드를 취소하는 게 매우 무례했습니다. 취소해야 할 때는 기냥 바로 끼어들어서 취소해버렸기 때문이죠. 그렇게 하면, 데이터 업데이트가 이뤄지는 도중에 취소되어서 부분적으로만 데이터가 업데이트 된다든지, 자원해제가 제대로 안 된다든지 하는 부작용의 위험이 항상 존재합니다. 그래서 가능하면, 다른 방법이 전혀 없을 때, 이렇게 하는 것이 좋겠죠?

물론 기존의 방식도 여전히 활용가능하지만, 이젠 닷넷이 많이 예의를 갖췄습니다. 닷넷 4.0에 새롭게 추가된 PLINQ나 TPL을 사용하는 경우에는 취소 요청 접근법(cancellation request approach)만 사용할 수 있는데요, 이런 방식을 협력적인 취소(cooperative cancellation)이라고 합니다. 즉, 한 스레드가 다른 스레드를 강제로 종료시키는 게 아니라, 작업 취소 API를 통해서 작업을 취소해줄 것을 요청하는 것이죠. 취소 플래그를 통해서 취소요청을 받은 작업은 취소요청에 어떻게 응답할 것인지 선택할 수 있습니다. 예제를 하나 보시죠.

using System;
using System.Threading.Tasks;
using System.Threading;

namespace Exam11
{
    class Program
    {
        static void PrintDash(CancellationToken cancellationToken)
        {
            cancellationToken.Register(Canceled);

            while (!cancellationToken.IsCancellationRequested)
            {
                Console.Write("-");
            }
        }

        static void Canceled()
        {
            Console.WriteLine("작업이 취소되었군요!!");
        }

        static void Main(string[] args)
        {
            string stars = "*".PadRight(Console.WindowWidth - 1, '*');

            CancellationTokenSource cancellationTokenSource =
                new CancellationTokenSource();

            Task task = Task.Factory.StartNew(
                () => PrintDash(cancellationTokenSource.Token));
           
            Console.ReadLine();

            cancellationTokenSource.Cancel();
            Console.WriteLine(stars);
            task.Wait();
            Console.WriteLine("작업의 완료상태 : {0}", task.Status);
            Console.WriteLine();
        }
    }
}

<코드1> 취소 요청 접근법.

<코드1>은 그냥 평범하게 '-'를 출력하는 예제입니다. 하지만, 새로운 클래스가 몇개 보이는데요. CancellationTokenSource, CancellationToken클래스말이죠.

namespace System.Threading
{
    [ComVisible(false)]
    [DebuggerDisplay("IsCancellationRequested = {IsCancellationRequested}")]
    public struct CancellationToken
    {
        public CancellationToken(bool canceled);
       
        public static bool operator !=(CancellationToken left, CancellationToken right);

        public static bool operator ==(CancellationToken left, CancellationToken right);

        public bool CanBeCanceled { get; }

        public bool IsCancellationRequested { get; }

        public static CancellationToken None { get; }

        public WaitHandle WaitHandle { get; }

        public bool Equals(CancellationToken other);

        public override bool Equals(object other);

        public override int GetHashCode();

        public CancellationTokenRegistration Register(Action callback);

        public CancellationTokenRegistration Register(Action<object> callback, object state);

        public CancellationTokenRegistration Register(Action callback, bool useSynchronizationContext);

        public CancellationTokenRegistration Register(Action<object> callback, object state, bool useSynchronizationContext);

        public void ThrowIfCancellationRequested();
    }
}

<코드2> 구조체 CancellationToken.

CancellationToken클래스는 말 그대로, 현재 이 토큰이 어떤 상태에 있는지 모니터링 하기 위한 정보를 갖고 있습니다. 이 토큰이 현재 취소 요청을 받았는지, 취소요청을 받으면 어떤 행동을 취할 것인지 등을 확인하고, 설정할 수 있습니다.

namespace System.Threading
{
    [ComVisible(false)]
    public sealed class CancellationTokenSource : IDisposable
    {
        public CancellationTokenSource();

        public bool IsCancellationRequested { get; }

        public CancellationToken Token { get; }

        public void Cancel();

        public void Cancel(bool throwOnFirstException);

        public static CancellationTokenSource CreateLinkedTokenSource(params CancellationToken[] tokens);

        public static CancellationTokenSource CreateLinkedTokenSource(CancellationToken token1, CancellationToken token2);

        public void Dispose();
    }
}

<코드3> CancellationTokenSource 클래스

그리고 CancellationTokenSource는 CancellationToken의 기반이 되는 클래스로(Source라는 이름이 붙어있죠), CancellationTokenSource에서 생성된 각각의 Token에 대해서 취소를 요청하는 역할을 합니다. CancellationTokenSource에서 Cancel메서드로 취소요청을 하면, 같은CancellationTokenSource에서 생성된 Token들은 전부 취소요청을 받는 셈이죠.


한가지 주목해서 보실 점은 CancellationToken가 클래스가 아니라 구조체라는 것입니다. 즉, Token을 매번 다른 객체에 넘겨줄 때마다 새로운 복사본이 생성된다는 것이죠. 그래서 각각의 스레드에 넘겨진 Token은 각각 독립적인 복사본 이므로, Cancel메서드는 스레드 안전성(thread-safe)을 확보할 수 있습니다. 만약에 참조가 그냥 복사된다면, 각각의 스레드가 Token에 손을 대면, 다른 스레드가 참조하는 Token에도 동일한 변화가 생겨서 예측불가능한 일이 벌어지겠죠.

<코드1>을 보면, 병렬적으로 수행되는 작업에서 취소 요청을 모니터링하기 위해서, CancellationToken을 인자로 넘겨주는 것을 볼 수 있습니다. 그래서 PrintDash메서드 내부에서 IsCancellationRequested속성을 통해서 작업 취소 요청이 들어왔는지 계속 해서 확인하게 되죠. 그럼 <코드1>을 실행 해볼까요?

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--작업이 취소되었군요!!
*******************************************************************************
작업의 완료상태 : RanToCompletion

계속하려면 아무 키나 누르십시오 . . .

<결과1> <코드1>의 실행결과.

<결과1>을 보면, 작업의 완료상태를 출력하는 부분이 있는데요, 이 부분에서 RanToCompletion이 출력되고 있습니다. 그래서 만약, ContinueWith메서드로 연쇄 작업을 연결하고, 옵션을 OnlyOnCanceled로 설정해준다고 하더라도, 연쇄작업은 실행되지 않습니다. 작업은 완료된 상태이기 때문에, 연쇄 작업이 취소되었다는 에러메세지만 확인할 수 있을 뿐이죠. 그렇다면, 연쇄작업을 이용해서 <코드1>과 동일한 결과를 내려면 어떻게 해야 할까요?

using System;
using System.Threading.Tasks;
using System.Threading;

namespace Exam12
{
    class Program
    {
        static void PrintDash(CancellationToken cancellationToken)
        {
            while (!cancellationToken.IsCancellationRequested)
            {
                Console.Write("-");
            }

            if (cancellationToken.IsCancellationRequested)
            {
                cancellationToken.ThrowIfCancellationRequested();
            }
        }

        static void Main(string[] args)
        {
            string stars = "*".PadRight(Console.WindowWidth - 1, '*');

            CancellationTokenSource cancellationTokenSource =
                new CancellationTokenSource();

            Task task = Task.Factory.StartNew(
                () => PrintDash(cancellationTokenSource.Token),
                cancellationTokenSource.Token);

            Task canceledTask = task.ContinueWith(
                (antecedentTask) => Console.WriteLine("작업이 취소되었군요!!"),
                TaskContinuationOptions.OnlyOnCanceled);

            Console.ReadLine();

            cancellationTokenSource.Cancel();
            Console.WriteLine(stars);
            canceledTask.Wait();
            Console.WriteLine("작업의 완료상태 : {0}", task.Status);
            Console.WriteLine();
        }
    }
}

<코드4> <코드1>과 동일하지만, 연쇄작업을 사용하는 코드.

<코드4>가 바로, <코드1>과 동일한 결과를 내는 코드입니다.(사실 완전히 같지는 않습니다. 실행해보면, '작업이 취소되었군요!'라는 멘트가 출력되는 위치가 다르지요.) 이런식으로 연쇄작업을 연결해놓고, 병렬로 실행되는 메서드 안에서, ThrowIfCancellationRequested()메서드를 통해서, 취소되었을 때, 취소되었다는 표시를 하도록 한 것이죠. 그러면, 연쇄 작업이 바톤을 이어받아서 실행을 계속하게 됩니다. 그리고 또 한가지 차이점은 작업을 생성할 때, 인자로 Token을 넘겨준다는 것이지요.


- 마치면서.

요즘 월드컵을 보면 16강이 가능할 것 같다는 생각이 들기도 하는데요. 꼭! 갔으면 좋겠네요!! ......이게 마무리 멘트라니-_-!! 어헣.


- 참고자료

1. Essential C# 4.0, Mark Michaelis, Addison Wesley

Asynchronous Agents Library – agent. 2 ( 기능 )

VC++ 10 Concurrency Runtime 2010. 6. 13. 09:00 Posted by 알 수 없는 사용자

Asynchronous Agents Library – agent. 2 ( 기능 )

작성자: 임준환( mumbi at daum dot net )

 

void run();

Agent 클래스를 상속 받아 작업을 하는 agent 를 만들 때, 처음으로 해야 할 일은 run() 재정의입니다.

 run() 는 CPU 가 해당 agent 스레드의 컨텍스트를 처리할 때, 수행되는 메소드입니다. 즉, 바로 agent 가 책임지고 처리해야 할 작업( task )이고, run() 을 재정의하기 위해 agent class 가 존재한다고 해도 과언이 아닐 정도로 중요합니다.

 Asynchronous Agents Library( 이하 AAL )을 사용하지 않고 Win32 API 로 직접 스레드를 생성할 때, 지정하는 콜백 함수와 같은 역할을 합니다.

 run() 에 필요한 정보( 매개 변수 )가 있다면 agent 를 상속 받은 클래스의 생성자를 이용하여 전달하면 됩니다.

 run() 이 호출될 때, agent 의 상태는 agent_started 가 됩니다.

 run() 을 재정의할 때, 주의할 점은 run() 이 끝나기 전에 done() 을 호출해야 한다는 것입니다. 실제로 run() 이 끝났다는 것은 작업이 끝난 것이지만 상태는 여전히 agent_started 이기 때문에 계속 수행 중인 것으로 인식됩니다. 그러므로 agent 의 상태를 바꿔주기 위해 반드시 run() 이 끝나기 전에 done() 을 호출해야 합니다.

또한 run() 은 어떤 예외도 던지지 않습니다.

 

bool done();

Agent 의 작업이 완료되었음을 알립니다. 이것은 agent 의 상태를 agent_done 으로 바꾸는 것을 의미합니다.

제대로 agent_done 상태가 되면 true 를 반환합니다. cancel() 에 의해 agent_cancel 상태인 agentagent_done 상태가 되지 않고 false 를 반환합니다.

 protected 로 지정되어 있어 메소드 내에서만 호출할 수 있습니다.

 

bool start();

 start() 를 호출함으로써 CPU 스케줄에 의해 run() 이 호출되는 것입니다. run() 이 호출되기 위해서는 반드시 start() 를 호출해야 합니다. 직접 run() 을 호출하면 병렬 처리 또는 비 동기 처리되지 않고, 호출한 스레드의 컨텍스트에서 일반 함수를 호출한 것과 같게 됩니다.

그러므로 직접 run() 을 호출하는 일은 없어야 하며, 꼭 start() 를 호출하도록 해야 합니다.

 start() 는 agent 의 상태를 agent_created 에서 agent_runnable 로 바꿉니다. 즉, 스케줄하여 컨텍스트 스위칭( context switching ) 의 대상이 되도록 합니다.

Agent 가 제대로 스케줄 되었다면 true 를 반환합니다. 스케줄 되기 전( start() 호출 전 )에 cancel() 을 호출하면 스케줄 되지 않고 false 를 반환합니다.

 

bool cancel();

Agent 객체의 작업을 취소할 때 사용합니다.

Agent 객체가 생성되어 agent_created 상태가 되거나, start() 에 의해 agent_runnable 상태일 때에 작업을 취소하고 종료된 상태인 agent_cancel 상태로 바꿉니다.

다시 말해, run() 이 호출되어 agent_started 상태에서는 agent_cancel 상태로 바뀌지 않고 실패하여 false 를 반환합니다. 제대로 agent_cancel 상태로 바뀌었다면 true 를 반환합니다.

 

agent_status status();

Agent 객체의 현재 상태를 반환합니다.

 agent_statusenum 형으로 agent_canceled( 취소됨 ), agent_created( 생성됨 ), agent_done( 작업 완료 ), agent_runnable( 스케줄 됨 ), agent_started( 실행 중 ) 를 나타냅니다.

반환된 상태는 동기화되어 정확한 상태를 반환하지만, 반환하자마자 agent 의 상태가 변할 수 있어 반환된 상태가 현재 agent 의 상태라고 확신하면 안 됩니다.

 

ISource<agent_status> * status_port();

 status() 는 동기화된 agent 의 상태를 반환하는 반면, status_port() 는 비 동기 메커니즘은 message 를 통해 반환됩니다.

반환형인 ISource 는 message 메커니즘의 interface 입니다. ISource interface 형은 receive() 로 내용을 꺼내올 수 있습니다.

아직 message 에 대해서 언급하지 않았기 때문에 이해가 안 될 수 있습니다. 곧 message 에 대해서 설명할 것인데 그 때, 이 함수가 어떻게 동작하는지 알 수 있을 것입니다.

 

static agent_status wait( agent * _PAgent, unsigned int _Timeout = COOPERATIVE_TIMEOUT_INFINITE );

Win32 API 의 WaitForSingleObject() 와 같은 기능을 합니다.

인자로 넘긴 agent 의 작업이 종료 상태가 될 때까지 기다립니다. 종료 상태란 agent_cancel, agent_done 상태를 말합니다.

기다릴 최대 시간( timeout )을 정할 수 있는데 COOPERATIVE_TIMEOUT_INFINITE 는 무한대를 의미하며, 기본 값으로 지정되어 있습니다. 이 때, 최대 시간은 밀리 초( millisecond ) 단위입니다.

최대 시간을 정했을 경우, 최대 시간까지 agent 의 작업이 종료되지 않으면, operation_timed_out 예외가 발생합니다. 그러므로 최대 시간을 고려한 프로그래밍을 할 경우, 이 예외를 처리함으로써 최대 시간 이 후를 제어해야 합니다.

Agent 의 상태를 반환하는데, 이 상태는 agent_cancel 또는 agent_done 입니다. 왜냐하면 앞의 두 상태 중 하나가 되어야만 반환을 하기 때문입니다.

 

static void wait_for_all( size_t _Count, __in_ecount(_Count) agent ** _PAgents, __out_ecount_opt(_Count) agent_status * _PStatus = NULL, unsigned int _Timeout = COOPERATIVE_TIMEOUT_INFINITE );

Win32 API 의 WaitForMultipleObjects() ( 3번째 인자가 TRUE 인 )와 같은 기능을 합니다.

인자로 전달되는 agent 배열이 모두 완료되었을 때까지 기다립니다.

인자를 살펴보면,

  • _Count - 기다릴 agent 의 개수( 뒤의 인자인 agent 배열의 원소 수와 같아야 합니다. )
  • _PAgents - 기다릴 agent 들의 배열
  • _pStatus – 이 함수가 반환될 때, agent 들의 상태들을 저장할 배열( _Timedout 을 지정했을 때, operation_timed_out 예외가 발생한다면, 상태를 저장하는 작업은 수행되지 않습니다. ), 기본 값은 NULL 입니다.
  • _Timeout – 기다릴 최대 시간, 기본 값은 COOPERATIVE_TIMEOUT_INFINITE 이다.

 wait() 와 마찬가지로 기다릴 최대 시간을 지정할 경우, operation_timed_out 예외가 발생되며, 최대 시간을 고려할 경우, 이 예외를 처리해야 합니다.

 

static void wait_for_one( size_t _Count, agent ** _PAgents, agent_status& _Status, size_t& _Index, unsigned int _Timeout = COOPERATIVE_TIMEOUT_INFINITE );

Win32 API 의 WaitForMultipleObjects() ( 3번째 인자가 FALSE 인 )와 같은 기능을 합니다.

인자로 전달되는 agent 배열 중 하나라도 완료가 될 때까지 기다립니다.

인자를 살펴보면,

  • _Count – 기다릴 agent 의 개수( 뒤의 인자인 agent 배열의 원소 수와 같아야 합니다. )
  • _PAgent – 기다릴 agent 들의 배열
  • _Status – 반환될 때의 agent 상태를 저장할 agent_status 변수
  • _Index – agent 배열 중 완료된 agent 의 인덱스를 저장할 변수
  • _Timeout – 기다릴 최대 시간, 기본 값은 COOPERATIVE_TIMEOUT_INFINITE 입니다.

 wait() 와 마찬가지로 기다릴 최대 시간을 지정할 경우, operation_timed_out 예외가 발생하며, 최대 시간을 고려할 경우, 이 예외를 처리해야 합니다.

 

예제

아래의 코드는 위에 설명한 메소드들을 사용하는 상황을 보여줍니다. 특별한 시나리오는 없지만, 충분히 어떤 상황에 어떻게 사용하는지 알 수 있을 것입니다.

 TestAgentrun() 안에서 사용된 Concurrency::wait() 는 Win32 API 의 Sleep() 과 같은 기능을 합니다. agent::wait() 와 혼동하지 않기를 바랍니다.

예제를 위한 준비

#include <iostream>
#include <agents.h>

using namespace std;
using namespace Concurrency;

const wchar_t* GetAgentStatusString( agent_status status )
{
	switch( status )
	{
	case agent_created:
		return L"agent_created";

	case agent_runnable:
		return L"agent_runnable";

	case agent_started:
		return L"agent_started";

	case agent_done:
		return L"agent_done";

	case agent_canceled:
		return L"agent_canceled";

	default:
		return L"unknown";
	}
}

class TestAgent
	: public agent
{
private:
	unsigned int	id;

protected:
	virtual void run()
	{
		Concurrency::wait( this->id * 500 );		

		wcout << L"agent id: " << this->id << L" completed." << endl;

		done();
	}
public:
	TestAgent( unsigned int id )
		: id( id ) { }
};

[ 코드1. 예제를 위한 코드 ]

status_port() 의 사용

int main()
{	
	TestAgent testAgent( 5 );

	wcout << GetAgentStatusString( receive( testAgent.status_port() ) ) << endl;

	testAgent.start();
	
	for( unsigned int i = 0; i < 10; ++i )
	{		
		wcout << GetAgentStatusString( receive( testAgent.status_port() ) ) << endl;
	}

	agent::wait( &testAgent );

	wcout << GetAgentStatusString( receive( testAgent.status_port() ) ) << endl;
}

[ 코드2. status_port() 사용 예제 ]

 status() 와 같은 기능을 하지만 비 동기 메커니즘인 message 을 사용한다는 것이 다릅니다.

이 말은 status() 가 수행되는 동안 호출한 컨텍스트가 멈추어 있지만, status_port() 는 호출한 컨텍스트는 계속 진행되고, 다른 work 스레드의 컨텍스트가 상태를 처리하고, 그 결과를 message 로 받는 다는 말입니다.

[ 그림1. status_port() 사용 예제 실행 결과 ]


wait()의 사용

int main()
{
	TestAgent testAgent1( 3 );
	TestAgent testAgent2( 2 );
	TestAgent testAgent3( 1 );
	TestAgent testAgent4( 5 );
	TestAgent testAgent5( 4 );

	testAgent1.start();
	testAgent2.start();
	testAgent3.start();
	testAgent4.start();
	testAgent5.start();

	agent::wait( &testAgent1 );
}

[ 코드3. wait() 사용 예제 ]

각기 다른 5 개의 작업들을 수행하는 agent 들을 생성합니다. 각각의 생성자의 인자는 각 agent 의 id 이기도 하지만, 해당 작업들의 가중치이기도 합니다.

testAgent1 의 작업이 끝날 때까지 기다립니다. 곧바로 프로그램이 종료되므로 작업이 끝나지 않은 agent 들은 비정상적으로 종료됩니다. 보통 작업이 진행 중인 모든 agent 들이 완료될 때가지 기다리는 것이 좋습니다.

[ 그림2. wait() 사용 예제 실행 결과 ]


wait() 와 timeout

int main()
{
	TestAgent testAgent1( 3 );
	TestAgent testAgent2( 2 );
	TestAgent testAgent3( 1 );
	TestAgent testAgent4( 5 );
	TestAgent testAgent5( 4 );

	testAgent1.start();
	testAgent2.start();
	testAgent3.start();
	testAgent4.start();
	testAgent5.start();

	try
	{
		agent::wait( &testAgent4, 1000 );
	}
	catch( operation_timed_out& )
	{
		wcout << L"operation timed out." << endl;
	}
}

[ 코드4. wait() 와 timeout 예제 ]

 wait() 의 timeout 매개변수를 사용하려면 operation_timed_out 예외를 처리해야 합니다.

[ 그림3. wait() 와 timeout 예제 실행 결과 ]


wait_for_all() 의 사용

int main()
{
	TestAgent testAgent1( 3 );
	TestAgent testAgent2( 2 );
	TestAgent testAgent3( 1 );
	TestAgent testAgent4( 5 );
	TestAgent testAgent5( 4 );

	testAgent1.start();
	testAgent2.start();
	testAgent3.start();
	testAgent4.start();
	testAgent5.start();

	agent* runningAgents[5] = {
		&testAgent1,
		&testAgent2,
		&testAgent3,
		&testAgent4,
		&testAgent5
	};

	unsigned int runningAgentCount = sizeof( runningAgents ) / sizeof( runningAgents[0] );	

	agent::wait_for_all( runningAgentCount, runningAgents );
}

[ 코드5. wait_for_all() 사용 예제 ]

배열에 포함된 모든 agent 들이 완료될 때까지 기다립니다.

[ 그림4. wait_for_all() 사용 예제 실행 결과 ]


wait_for_all() 과 상태 반환

int main()
{
	TestAgent testAgent1( 3 );
	TestAgent testAgent2( 2 );
	TestAgent testAgent3( 1 );
	TestAgent testAgent4( 5 );
	TestAgent testAgent5( 4 );

	testAgent1.start();
	testAgent2.start();
	testAgent3.start();
	testAgent4.start();
	testAgent5.start();

	agent* runningAgents[5] = {
		&testAgent1,
		&testAgent2,
		&testAgent3,
		&testAgent4,
		&testAgent5
	};

	agent_status statuses[5];

	unsigned int runningAgentCount = sizeof( runningAgents ) / sizeof( runningAgents[0] );
	agent::wait_for_all( runningAgentCount, runningAgents, statuses );

	for( unsigned int i = 0; i < runningAgentCount; ++i )
		wcout << GetAgentStatusString( statuses[ i ] ) << endl;
}

[ 코드6. wait_for_all() 과 상태 반환 예제 ]

완료된 agent 들의 상태를 저장하는 배열의 크기는 agent 들을 포함한 배열의 크기와 같아야 합니다다. 만약 timeout 예외를 사용한다면 상태는 저장되지 않습니다.

[ 그림5. wait_for_all() 과 상태 반환 예제 실행 결과 ]


wait_for_one() 의 사용

int main()
{
	TestAgent testAgent1( 3 );
	TestAgent testAgent2( 2 );
	TestAgent testAgent3( 1 );
	TestAgent testAgent4( 5 );
	TestAgent testAgent5( 4 );

	testAgent1.start();
	testAgent2.start();
	testAgent3.start();
	testAgent4.start();
	testAgent5.start();

	agent* runningAgents[5] = {
		&testAgent1,
		&testAgent2,
		&testAgent3,
		&testAgent4,
		&testAgent5
	};

	agent_status finalStatus;
	unsigned int indexOfcompletedAgentFirst;

	unsigned int runningAgentCount = sizeof( runningAgents ) / sizeof( runningAgents[0] );	
	agent::wait_for_one( runningAgentCount, runningAgents, finalStatus, indexOfcompletedAgentFirst );

	wcout << L"final status : " << GetAgentStatusString( finalStatus ) << endl;
	wcout << L"index of completed agent first : " << indexOfcompletedAgentFirst << endl;	
}

[ 코드7. wait_for_one() 사용 예제 ]

여러 agent 들 중 하나라도 완료 되었을 때 반환됩니다. 상태 저장을 위한 변수와 인덱스 저장을 위한 변수는 참조로 넘겨야 하므로 생략할 수 없습니다.

[ 그림6. wait_for_one() 사용 예제 실행 결과 ]


 

마치는 글

위의 내용을 모두 숙지했다면 agent 를 마치 하나의 work 스레드처럼 다룰 수 있을 것입니다. 이렇게 하여 비 동기 병렬 처리를 쉽게 처리할 수 있습니다.

하지만 이것이 끝이 아닙니다. agent 에 message 메커니즘을 적용한다면 더욱 더 지능적인 agent 를 쉽게 만들 수 있습니다.

다음 글에서는 message 메커니즘에 대해 소개해 보도록 하겠습니다.

REMIX10 의 VS2010 팀 후기

VSTS 2010 팀 블로그 2010. 6. 11. 11:30 Posted by POWERUMC

2010년 6월 1일. REMIX10 행사는 2010년부터 장차 이끌어갈 Microsoft 기술이 총 집합하는 기술, IT 행사입니다. 이 날 REMIX10 의 큰 컨셉은 웹, 모바일, 개발 도구의 큰 획을 긋는 분야를 총 망라한 행사이기도 합니다.    

이 날, Visual Studio 2010 공식 팀은 Microsoft Korea 에서 많은 도움을 주셨던 터라, 좋은 자리에서 여러분들에게 시연 및 데모, 질의 응답을 받았습니다. REMIX10 의 Track 03 은 Visual Studio 2010 공식 팀에서 Visual Studio 2010 과 Team Foundation Server 2010, C# 4.0 을 주제로 세션이 진행되었습니다.    

Visual Studio 2010 이 중요한 이유는 단 한가지 입니다. 여러분들이 하고자 하는 모든 목표는 Visual Studio 2010 이 필요로 한다는 것입니다. 그리고 Visual Studio 2010 을 이용할 때 가장 큰 성과를 이루고 목표에 도달하기 쉬운 방법이기도 합니다. 여러분들의 개발 환경이 정확히 어떤지는 모르겠지만(대략만 알고 있습니다), Visual Studio 2010 을 써본 이상 다시는 그 이전으로 돌아가고 싶지 않을 거라고 장담합니다.^^    

이곳은 REMIX10 행사의 저희 팀 부스입니다. 행사장으로 진입하기 위해 계단을 내려오면 처음으로 보이는 첫 번째 오른쪽 부스입니다. 여러분들이 마음껏 Visual Studio 2010 을 사용해 볼 수 있는 노트북이 3대가 준비가 되어있습니다.

저희 Visual Studio 2010 공식 팀은 팀 블로그와 트위터를 운영하고 있습니다. 아직 저희 팀의 트위터를 모르고 계시는 분들이 많으신 것 같은데요. @vsts2010 을 팔로우 하시면 어느 곳보다 빠르게 소식과 정보를 받아보실 수 있답니다. 트위터를 통해 정보, 소식뿐만 아니라 여러분들이 현업에서 겪고 계시는 고충을 해결해 드리기 위해 기술 업체 및 Microsoft Korea 의 직원들과 연결해 주는 매체가 되도록 노력하겠습니다.

 

팀 부스에서 한 가지 이벤트를 진행했습니다. "Visual Studio 2010 에게 한마디" 라는 이벤트 인데요. Visual Studio 2010 에게 하고 싶은 얘기를 포스트잇으로 적어서 부스의 대형 모니터에 붙여주시면 가장 멋진 포스트잇을 선정하여 선물을 증정했던 이벤트 입니다. 부스에 마이크 시설이 갖추어 지지 않아 소규모로 진행한 이벤트였는데, 각 부스 운영진들이 선정한 포스트잇을 보여드립니다.

소규모로 진행된 이벤트라 연락처 기재를 요청하였고 연락처를 기재해 주신 분들에게 개별적으로 연락을 드려 선물을 증정해 드렸습니다. 말씀 드린대로 저희 부스의 운영진 각각 하나씩 마음에 드는 포스트잇을 선정하였습니다.

 

그 밖에, 이벤트에 많이 참여해 주신 분들 개별적으로 감사의 말씀을 전해드리지 못해서 너무 죄송스런 마음 뿐이랍니다.^^; 다음에 더 좋은 기회로 더 많은 선물을 드릴 수 있는 날이 조만간 올겁니다. ^^    

멋진 포스트잇을 적어주셔서 당첨되신 분들 축하 드립니다. 당첨되신 분들께서 저희 팀 블로그에 사진 공개를 허락해 주셨기에 이렇게 작은 소정의 경품을 전달해 드리는 사진을 공개해 드립니다.    

오른쪽 MSP 출신이신 김태균님께서 경품을 전달해 드리는 컷!

 

오른쪽 한국 최초이자 최고의 C++0x 전도사인 최흥배님께서 경품을 전달해 드리는 컷!

 

오른쪽 엄준일님(저^^)이 경품을 전달해 드리는 컷! 참고로 책 경품은 한국에서 ASP.NET 의 최고봉인 역자 김태영 이사님의 친필 사인을 받은 책이랍니다.

  

REMIX10 은 서울 행사 뿐만 아니라, 대전, 부산을 전국 투어 하면서 전국 행사를 마지막까지 잘 끝내주신 Microsoft Korea 분들과 저희 Visual Studio 2010 팀에게 감사의 말씀을 전해드립니다.

특히 Visual Studio 2010 팀 부스를 운영해 주신 좌로부터 엄준일, 강보람, 박세식, 김태균, 강뚱(Microsoft Korea 강성재 차장님), 최흥배님. 그리고 모든 관계자 분들에게 감사의 말씀을 전해 드립니다.

 

저희 Visual Studio 2010 공식 팀은 어느 날 갑자기 출범한 팀이 절대 절대로~ 아니랍니다. 오늘의 Visual Studio 2010 런칭이 있기 까지 Microsoft Korea 의 강성재 차장님을 주축으로 약 1년 6개월 전부터 기반을 준비한 팀이랍니다. 현재 20여명이 넘는 전문가들이 저희 팀에서 활동 중이며 앞으로의 활동에 많은 기대 부탁 드리겠습니다.    

참고로 REMIX10 키노트는 행사의 모든 것을 눈에 있는 2시간의 세션입니다. 만약 REMIX10 참석하지 못했다면 얼마나 우리들이 숨가쁘게 미래를 열어가는지 반드시 녹화된 비디오를 시청해 주시기 바랍니다. 아래의 녹화된 비디오는 Smooth Streaming 서비스를 이용하여 네트워크 상황에 최적화되어 끊김 없는 영상을 있습니다.
http://www.visitmix.co.kr/remix10/remix_live.html

감사합니다.

[Step.02-2] 클래스(class), 핸들(^), 그리고 구조체(struct)

C++/CLI 2010. 6. 11. 08:30 Posted by 알 수 없는 사용자

 

gcnew로 생성하지 않기

 

C++/CLI는 클래스를 생성할 때 ‘gcnew’를 사용하지 않고 생성할 수도 있습니다.

 

< 리스트 1. ‘gcnew’를 사용하지 않고 클래스 생성하기 >

#include "stdafx.h"

#include <stdio.h>

 

using namespace System;

 

ref class ManagedTest

{

public:

           ManagedTest() { Console::WriteLine(L"New ManagedTest"); }

           ~ManagedTest() { Console::WriteLine(L"delete ManagedTest"); }

          

           void func() { Console::WriteLine(L"Call func() - {0}", nNumber ); }

 

           int nNumber;

};

 

void foo1()

{

           ManagedTest MTest;

           MTest.nNumber = 1;

           MTest.func();

}

 

void foo2()

{

           ManagedTest^ MTest = gcnew ManagedTest();

           MTest->nNumber = 2;

           MTest->func();

}

 

int main(array<System::String ^> ^args)

{

           foo1();

           foo2();

          

           getchar();

           return 0;

}


< 결과 >


<리스트 1> ManagedTest MTest; 는 비 관리 C++처럼 GC가 아닌 스택 영역에 생성하는 것으로 착각할 수도 있겠지만 전혀 아닙니다. 클래스를 생성하면 언제나 GC 영역에 만들어집니다.

위의 코드는 그냥 ‘gcnew’ 사용을 우리가 생략하고 컴파일러가 대신 써 준다고 생각하시면 됩니다.

 

void foo1()

{

           ManagedTest MTest;

           MTest.nNumber = 1;

           MTest.func();

}

은 컴파일러에 의해서 아래의 코드로 바뀝니다.

void foo1()

{

           ManagedTest^ MTest = gcnew ManagedTest();

           MTest->nNumber = 1;

           MTest->func();

           delete MTest;

}

 

위의 코드를 보시면 아시듯이 gcnew를 사용하지 않고 클래스를 생성하면 우리가 직접 delete를 쓰지 않아도 되는 편리함을 얻을 수 있습니다.

gcnew를 사용하지 않는 것은 C#에서 ‘using’을 간략화 시킨 것으로 생각하면 좋습니다.


< C# using 문 사용 예 >

using (Graphics g = this.CreateGraphics())

{

    g.DrawLine(Pens.Black, new Point(0,0), new Point(3,5));

}

 

 

 

 

value 클래스

 

관리 클래스는 복사 생성자와 대입 연사자를 가지지 못하므로 아래의 코드는 컴파일 에러가 발생합니다.

 

< 리스트 2. >

#include "stdafx.h"

#include <stdio.h>

 

using namespace System;

 

ref class C {

    int i;

};


void func(C c) {}


int main()

{

    C c;

    C d;

    func(c);   // 에러

    d = c;     // 에러

}

 

그러나 클래스를  ‘ref’가 아닌 ‘value’ 클래스로 정의하면 위의 코드는 컴파일 할 수 있습니다.

 

#include "stdafx.h"

#include <stdio.h>

 

using namespace System;

 

value class C {

    int i;

};

void func(C c) {}

int main()

{

    C c;

    C d;

    func(c);   // 에러

    d = c;     // 에러

}

 

value 클래스는 클래스간 복사를 할 수 있는 능력이 있습니다. 이 복사는 비트 단위의 복사로 이른바 ‘memcpy’와 같은 복사입니다.

value 클래스는 복사를 할 수 있으므로 value 클래스의 멤버는 절대 복사 가능한 멤버만 가질 수 있습니다. 그래서 아래와 같은 value 클래스 C는 컴파일 에러가 발생합니다.

 

ref class A

{

  int i;

};

 

value class C

{

  A a;

};

 

 

value 클래스의 특징으로는 ref 클래스가 GC에서 만들어지는 것과 달리 스택에 만들 수 있습니다.

value class C

{

};

 

C c;

 

위 코드에서 C 클래스는 스택에 만들어집니다(물론 gcnew를 사용하면 GC에 만들어집니다)

 

value 클래스의 특징은 좀 더 있는데 위에 설명한 것들과 포함해서 아래와 같이 정리할 수 있습니다.

 


value 클래스의 특징

 

1. 기본 생성자를 가질 수 없다.

2. 복사 생성자를 가질 수 없다.

3. 대입 연산자를 가질 수 없다.

4. 소멸자를 가질 수 없다.

5. finalize를 가질 수 없다.

6. 클래스간 복사를 할 수 있다.

7. 복사 불가능한 것을 멤버로 가질 수 없다(ref 클래스 등).

8. 스택에 생성할 수 있다.

9. 다른 클래스를 계승할 수 없다( interface는 가능하다)

 

 

 


관리 클래스를 파라메터로 넘기기

 

ref class A

{

           int i;

};

 

void foo1( A a )

{

}

 

함수 foo1의 파라미터 정의는 클래스 A value 클래스일 때만 사용할 수 있습니다. 그러므로 아래와 같이 foo1의 파라미터를 정의해야 합니다.

void foo1( A^ a )

{

}

 

 

 

 

그 외….

 

C++/CLI에서 참조는 ‘%’을 사용합니다. 이것은 비 관리의 ‘&’와 구별이 됩니다.

%는 아래와 같은 경우에 유용하게 사용할 수 있습니다.

void foo( A^ a )

{

}

 

A a;

// foo( a );  // 에러

foo( %a ); // 성공


참조는 C#에서는 'ref', VB.NET에서는 'ByRef'와 같다고 생각하시면 됩니다.


// 값을 참조로 넘기는 경우

void valuebyref(int%i)
{
   i=5;
}

// 참조형을 참조로 넘기기
void refbyref(String^%s)
{
   s="newstring";
}

void main()
{
   int i=1;

   valuebyref(i);

   String^s="basestring";
   refbyref(s);
}
( 위 코드는 http://adversaria-june.blogspot.com/2006/08/ccli_26.html 에서 인용했습니다 )

 



구조체


C++/CLI에서의 구조체가 클래스와 다른 점

1. 'value class'와 같습니다. 물론 비 관리코드에서는 기존의 C++과 같다

2. 구조체는 상속 받을 수 없다.

 

 

 

 

Welcome to Parallel C#(6) - To be continue...

C# Parallel Programming 2010. 6. 10. 09:00 Posted by 알 수 없는 사용자

- To be continue??

한 때, 뮤직비디오를 드라마 처럼 만드는 게 유행했던 때가 있었습니다. 기억하시나요? 한창 조성모가 데뷔했을 때로 기억하는데요. 노래 한곡에 뮤직비디오가 여러편이 있어서, 하나가 공개 되고 나면, 'To be continue..'라는 자막이 나오면서 애간장을 태우곤 했었죠. 그게 오늘 내용과 무슨 상관이냐!?!? 별 상관없습니다-_- 어허허헣.


- 뒤를 부탁해, ContinueWith

지금까지는 작업을 만들어서 돌리고, 작업이 종료되면 그냥 끝났었는데요. 한 작업이 어떤 상태로 끝났을 때, 그 상태에 따라서 다른 작업이 연쇄적으로 일어나도록 설정하고 싶은 경우가 있겠죠. Task클래스에는 그런 경우를 위해서 ContinueWith라는 메서드가 준비되어 있습니다. 낄낄낄. 이 메서드는 선행 작업(antecedent task)에 하나 이상의 연쇄적인 작업을 연결 시켜놓고, 선행 작업의 상태나 종료에 따라서 연쇄적으로 이후 작업이 실행되도록 설정할 수 있도록 해줍니다. 그러면, 선행 작업의 상태에 대해서 설정가능한 상태가 뭐가 있는지 한번 확인해볼까요?

 상태  설명 
 None  아무 값도 명시되지 않으면 사용되는 기본 값으로, 선행작업의 상태와 관계없이, 선행작업이 종료되면 바로 시작된다.
 NotOnRanToCompletion  선행작업이 끝난 상태에서는 연쇄작업을 실행계획에 넣지 않는다.
 NotOnFaulted  선행작업이 처리안된 예외를 던지면, 연쇄작업을 실행계획에 넣지 않는다.
 NotOnCanceled  선행작업이 취소되면, 연쇄작업을 실행계획에 넣지 않는다.
 OnlyOnRanToCompletion  선행작업이 정상적으로 완료된 상태에서만 연쇄작업을 실행계획에 추가시킨다.
 OnlyOnFaulted  선행작업이 처리안된 예외를 던지는 상태에서만 연쇄작업을 실행계획에 추가시킨다.
 OnlyOnCanceled  선행작업이 취소된 상태에서만 연쇄작업을 실행계획에 추가시킨다.
<표1> http://msdn.microsoft.com/en-us/library/system.threading.tasks.taskcontinuationoptions.aspx에서 참조한 상태값과 설명

그리고 ContinueWith메서드는 Task타입의 객체를 리턴하는 데요, 그 덕분에 연쇄적으로 작업에 작업을 계속해서 추가할 수도 있습니다. 그러면, 예제를 한번 확인해볼까요?

using System;
using System.Threading.Tasks;

namespace Exam9
{
    class Program
    {
        static string Calc(object from)
        {
            long sum = 0;
            long start = (long)from;
            Console.WriteLine("현재 이 메서드를 실행중인 스레드 ID : {0}",
                Task.CurrentId);

            for (long i = start; i < 100000000; i++)
            {
                sum += i;
                if(i == 100000 || i == 200000)
                {
                    //100000에 이르면, 그냥 실행을 종료시킨다.
                    throw new ApplicationException("그냥 에러가 났음");
                }
            }
            Console.WriteLine("계산 끝");
            return sum.ToString();
        }

        static void Main(string[] args)
        {
            Task<string> task = new Task<string>(
                Calc, 1L);

            task.Start();

            Task<string> justDoIt = task.ContinueWith<string>(
                (antecedentTask) =>
                {
                    Console.WriteLine("이전 작업 상태 : {0}", antecedentTask.Status);
                    return Calc(100001L);
                },
                TaskContinuationOptions.None);

            Task<string> justDoIt2 = justDoIt.ContinueWith<string>(
                (antecedentTask) =>
                {
                    Console.WriteLine("이전 작업 상태 : {0}", antecedentTask.Status);
                    return Calc(200001L);
                },
                TaskContinuationOptions.None);

            try
            {
                Console.WriteLine(task.Result);
            }
            catch (AggregateException ex)
            {
                foreach (var item in ex.InnerExceptions)
                {
                    Console.WriteLine("에러 : {0}", item.Message);
                }
            }
            finally
            {
                try
                {
                    Console.WriteLine(justDoIt.Result);
                }
                catch (AggregateException ex)
                {
                    foreach (var item in ex.InnerExceptions)
                    {
                        Console.WriteLine("에러 : {0}", item.Message);
                    }
                }
                finally
                {
                    Console.WriteLine(justDoIt2.Result);
                    Console.WriteLine("끝");
                }
            }
        }
    }
}

<코드1> 연쇄작업 물리기.

<코드1>은 Calc메서드를 실행하는 작업을 하나 시작하고, 그 작업에 연쇄작업을 하나 연결하고, 그 연쇄작업에 또 연쇄작업을 하나 연결한 예제입니다. 연쇄작업의 옵션을 보면, 전부 'TaskContinuationOptions.None'로 되어 있는데요. <표1>에서 확인해보면, 선행작업이 어떤 식으로 종료되든, 종료가 되면 이어서 실행하도록 하는 옵션이죠. 그리고 Calc메서드를 보면, for루프의 카운트가 100000과 200000에 이르면, 예외를 던지고 실행을 종료하도록 했습니다. 그리고 작업을 보면, task는 1부터, justDoIt은 100001부터 실행하므로 둘다 예외를 만나겠죠. 그리고 마지막 justDoIt2는 200001부터 실행하므로 실행을 종요하게 됩니다. 그럼 결과를 보죠.(설명을 위한 예제이므로 실제로 이렇게 짜면 곤란하겠죠)

현재 이 메서드를 실행중인 스레드 ID : 1
이전 작업 상태 : Faulted
현재 이 메서드를 실행중인 스레드 ID : 2
이전 작업 상태 : Faulted
현재 이 메서드를 실행중인 스레드 ID : 3
에러 : 그냥 에러가 났음
에러 : 그냥 에러가 났음
계산 끝
4999979949900000

계속하려면 아무 키나 누르십시오 . . .
<결과1> 연쇄작업 실행 결과.

우리가 예측한대로, 선행작업이 실패하면서, 바로 다음작업이 연쇄적으로 실행되는 걸 확인할 수 있습니다. 지난 포스트에서 Wait메서드를 통해서 추가적으로 생성된 스레드의 결과를 기다리거나, Result속성을 통해서 결과값을 요청하지 않으면 추가스레드의 결과는 그냥 무시된다고 했었는데요. 연쇄작업도 마찬가지 입니다. 연쇄작업을 연결해 놓았다고 해도, Wait로 기다리거나 Result를 요구하지 않으면 그냥 무시되어 버리는 것이죠. <코드1>에서 justDoIt2.Result부분을 아래 처럼 주석처리하고 실행해보죠.

finally
{
    //Console.WriteLine(justDoIt2.Result);
    Console.WriteLine("끝");
}
<코드2> 주석 처리.

그리고 결과는 보면,

현재 이 메서드를 실행중인 스레드 ID : 1
이전 작업 상태 : Faulted
현재 이 메서드를 실행중인 스레드 ID : 2
이전 작업 상태 : Faulted
현재 이 메서드를 실행중인 스레드 ID : 3
에러 : 그냥 에러가 났음
에러 : 그냥 에러가 났음

계속하려면 아무 키나 누르십시오 . . .
<결과2> 작업이 무시된 결과

justDoIt2가 진행하던 작업의 결과는 무시된 걸 확인할 수 있습니다.

<표1>에서 None옵션은 선행작업이 어떤 이유로 종료가 되었든, 종료가 되었다면 바로 이어서 연쇄작업을 실행하도록 하는 옵션인데요. 그러면, <코드1>과 동일한 결과를 가져오려면 어떤 옵션을 사용하면 되는 걸까요? 한번 생각해보시죠. 어헣. 힌트는 예외를 던지고 종료되는 작업의 상태를 보시면 바로 정답이 나옵니당. 정답은 <코드3>을 보시죠!

Task<string> justDoIt = task.ContinueWith<string>(
    (antecedentTask) =>
    {
        Console.WriteLine("이전 작업 상태 : {0}", antecedentTask.Status);
        return Calc(100001L);
    },
    TaskContinuationOptions.OnlyOnFaulted);

Task<string> justDoIt2 = justDoIt.ContinueWith<string>(
    (antecedentTask) =>
    {
        Console.WriteLine("이전 작업 상태 : {0}", antecedentTask.Status);
        return Calc(200001L);
    },
    TaskContinuationOptions.OnlyOnFaulted);

<코드3> 같은 결과를 가져오는 코드

그렇씁니다. OnlyOnFaulted는 선행작업이 처리안된 예외를 던지는 상태에서만 연쇄작업을 실행시키는 옵션이죠. 어허허허헣.

그렇다면, 작업의 성공이나 실패를 자동으로 통지하도록 하는 방법을 사용할 수 있을 것 같습니다.

using System;
using System.Threading.Tasks;

namespace Exam10
{
    class Program
    {
        static string Calc(object from)
        {
            long sum = 0;
            long start = (long)from;
            Console.WriteLine("현재 이 메서드를 실행중인 스레드 ID : {0}",
                Task.CurrentId);

            for (long i = start; i < 100000000; i++)
            {
                sum += i;
                if (i == 100000)
                {
                    //100000에 이르면, 그냥 실행을 종료시킨다.
                    throw new ApplicationException("그냥 에러가 났음");
                }
            }
            Console.WriteLine("계산 끝");
            return sum.ToString();
        }

        static void Main(string[] args)
        {
            Task<string> task = new Task<string>(
                Calc, 1L);

            task.Start();

            Task completedTask = task.ContinueWith(
                (antecedentTask) =>
                {
                    Console.WriteLine("Task State: 무사 완료!!");
                    //작업 완료 후에 이어서 뭔가를 처리
                },
                TaskContinuationOptions.OnlyOnRanToCompletion);

            Task faultedTask = task.ContinueWith(
                (antecedentTask) =>
                {
                    Console.WriteLine("Task State: 처리되지 않은 예외 발견!!");
                },
                TaskContinuationOptions.OnlyOnFaulted);

            try
            {
                completedTask.Wait();
                faultedTask.Wait();
            }
            catch (AggregateException ex)
            {
                foreach (var item in ex.InnerExceptions)
                {
                    Console.WriteLine("에러 : {0}", item.Message);
                }
            }
        }
    }
}

<코드4> 연쇄 작업으로 상태를 감지

<코드4>를 보시면, 작업이 무사히 끝났을 때와 처리안된 예외가 생겨서 종료될 때에 실행되도록 연쇄작업을 두개 연결해 놓았습니다. 일단 결과를 보면요,

현재 이 메서드를 실행중인 스레드 ID : 1
Task State: 처리되지 않은 예외 발견!!
에러 : 작업이 취소되었습니다.
계속하려면 아무 키나 누르십시오 . . .
<결과3>

예상대로, OnlyOnFaulted로 설정된 연쇄작업이 실행된 것을 확인할 수 있습니다. 왜냐면 작업을 1부터 시작하도록 했기 때문에, 카운트가 100000에 다다르면, 처리안된 예외가 발생하기 때문이죠. 작업의 상태값을 100001L이상으로 설정하면, OnlyOnRanToCompletion으로 설정된 연쇄작업이 실행되겠죠.

그런데, 두 경우다 예외가 하나씩 잡히는 걸 확인할 수 있습니다. '작업이 취소되었습니다'라는 예외인데요. 이유는 간단합니다. task에 OnlyOnRanToCompletion와 OnlyOnFaulted인 연쇄작업 두개를 연결했기 때문에, 둘 중 하나만 항상 실행이 됩니다. 그래서 둘 중에 하나는 실행이 되지 못한채, 선행작업이 끝나버리는 거죠. 그래서 Wait메서드를 통해서 기다리고 있던 작업은 종료되지 못하고 취소가 되는 마는 것이죠.


- 마무리

오늘은 선행작업의 상태에 따라서 연쇄적으로 작업을 실행하는 예제를 살펴봤습니다. 다음에 뵙쬬!


- 참고자료

1. Essential C# 4.0, Mark Michaelis, Addison Wesley

- 인생이 원하는 대로 가지는 않더라.

우리는 인생을 살면서, 여러가지 계획을 세웁니다. 하지만, 보통 계획을 세울 때, 예외적인 상황을 감안하지 않는 경우가 많습니다. 그래서 늘 일정은 실패로 끝나게 되고, '아, 난 안되나 보다.'하고 절망하게 되는 것이죠. 자세한 내용은 최인철 교수님의 '프레임'을 참고하시면..... 순간 책 소개 코너로 빠져들뻔 했군요. 어헣.

아무튼, 우리는 인생에서 뿐만 아니라 프로그래밍에서도 생각외의 순간을 많이 만나게 됩니다. '도대체 어떤 **가 이런 거 까지 해볼까?'하는 생각으로 안이하게 프로그래밍을 하다보면 기상 천외한 버그 리포트를 받게 됩니다. 그래서 예외 처리가 중요한 것이죠. 오늘은 병렬 프로그래밍에서 예외 처리 하는 법에 대해서 간단하게 이야기를 해보려고 합니다.


- 차이점 하나.

기존의 프로그래밍에서는 그저 예외를 발생시 처리하고 싶은 구문을 try로 감싸면 되었는데요. 병렬 프로그래밍에서는 어떨까요? Task.Start()를 try로 감싸면 결과를 얻을 수 있을까요? 한번 실험해보죠.

using System;
using System.Threading.Tasks;

namespace Exam5
{
    class Program
    {
        static string Calc(object from)
        {
            long sum = 0;
            long start = (long)from;
            Console.WriteLine("현재 이 메서드를 실행중인 스레드 ID : {0}",
                Task.CurrentId);

            for (long i = start; i < 100000000; i++)
            {
                sum += i;
                if (i == 100000)
                {
                    //100000에 이르면, 그냥 예외를 던진다ㅋ.
                    throw new ApplicationException("그냥 에러가 났음");
                }
            }
            Console.WriteLine("계산 끝");
            return sum.ToString();
        }

        static void Main(string[] args)
        {
            Task<string> task = new Task<string>(
                Calc, 1L);

            try
            {
                task.Start();
            }
            catch (AggregateException ex)
            {
                foreach (var item in ex.InnerExceptions)
                {
                    Console.WriteLine("에러 : {0}", item.Message);
                }
            }

            Console.WriteLine(task.Result);
        }
    }
}

<코드1> Start를 try로 감싸기

<코드1>을 보시면, task.Start()를 try로 감싸고 있습니다. 과연 실행중에 던지는 예외를 잘 받을 수 있을까요? 결과는 아래와 같습니다.

현재 이 메서드를 실행중인 스레드 ID : 1

처리되지 않은 예외: System.AggregateException: 하나 이상의 오류가 발생했습니다.
---> System.ApplicationException: 그냥 에러가 났음
   위치: Exam5.Program.Calc(Object from) 파일 C:\Users\boram\Documents\Visual St
udio 10\Projects\Chapter9\Exam7\Program.cs:줄 21
   위치: System.Threading.Tasks.Task`1.InvokeFuture(Object futureAsObj)
   위치: System.Threading.Tasks.Task.InnerInvoke()
   위치: System.Threading.Tasks.Task.Execute()
   --- 내부 예외 스택 추적의 끝 ---
   위치: System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCance
ledExceptions)
   위치: System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, Cancellatio
nToken cancellationToken)
   위치: System.Threading.Tasks.Task`1.get_Result()
   위치: Exam5.Program.Main(String[] args) 파일 C:\Users\boram\Documents\Visual
Studio 10\Projects\Chapter9\Exam7\Program.cs:줄 45
계속하려면 아무 키나 누르십시오 . . .

<결과1> <코드1>의 실행 결과

<결과1>을 보면, 예외는 전혀 잡히지 않았습니다. 왜 일까요? 작업에서 실행하는 코드(여기서는 Calc메서드)는 Start메서드 내에서 실행되는 게 아니라, Start메서드로 작업이 시작된 이후에야 별도로 시작되기 때문이죠. 그래서 Start메서드에 try를 걸어봤자 예외는 잡을 수 없습니다.

가만히 생각해보면, 작업 안에서 처리되는 예외(즉, Calc메서드 내에서 발생하고 처리되는 예외)는 전혀 고민할 필요가 없겠죠. 하지만, 처리되지 못한 예외가 발생하는 경우는 바깥에서 처리할 방법을 찾아야 합니다.

CLR 2.0 버전까지는 이런 처리되지 못한 예외가 발생하면, 예외가 버블링되면서, 상위 계층으로 전파되면서 결국에는 윈도우 에러 보고 대화상자를 열게 만들고, 프로그램은 종료되었습니다. 하지만, 작업 내부에서 처리되지 못한 예외라고 하더라도, 작업 바깥에서 처리할 수 있는 방법이 있다면, 프로그램이 종료되는 것 보다는 바깥에서 처리할 수 있게 하는 게 더 나은 방법이겠죠.

작업내부에서 처리 안된 예외(unhandled exception)가 발생했다면, 그 예외는 일단 작업을 마무리 짓는 멤버(Wait(), Result, Task.WaitAll(), Task.WaitAny())가 호출되기 전까지는 조용히 기다립니다. 그리고 마무리 멤버의 호출에서 처리 안된 예외가 발생하게 되는 것이죠. <코드1>에서 Start메서드를 try블록으로 감쌌지만, 예외를 잡을 수 없었던 이유가 바로 여기에 있습니다. 처리 안된 예외는 마무리 멤버와 함께 발생하기 때문이죠. 그러면, <코드1>을 수정해서, 예외를 제대로 잡도록 수정해보겠습니다.

task.Start();

try
{
    Console.WriteLine(task.Result);
}

catch (AggregateException ex)
{
    foreach (var item in ex.InnerExceptions)
    {
        Console.WriteLine("에러 : {0}", item.Message);
    }
}

<코드2> Result를 try로 감싸라.

<코드2>를 보면, 작업을 마무리 짓는 멤버 중의 하나인, Result를 try블록으로 감싸고 있습니다. 그리고 결과를 보면,

현재 이 메서드를 실행중인 스레드 ID : 1
에러 : 그냥 에러가 났음
계속하려면 아무 키나 누르십시오 . . .
<결과2> 제대로 처리된 예외.

예외가 제대로 처리 된 것을 확인할 수 있습니다.


- 차이점 둘.

혹시 <코드1>, <코드2>를 주의 깊게 보신 분이라면, 처음보는 예외 하나를 발견하셨을지도 모르겠습니다. 바로 AggregateException인데요, 이에 대해서 이야기를 좀 해보겠습니다.

AggregateException은 닷넷 프레임워크 4.0에서 처음 추가된 예외 타입인데요, MSDN의 설명을 보면(http://msdn.microsoft.com/en-us/library/system.aggregateexception.aspx), '프로그램 실행 중에 발생하는 하나 또는 여러개의 에러를 표현하는 수단'이며, 주로 TPL과 PLINQ에서 활용되고 있다고 합니다.

aggregate는 여러 개의 작은 것들을 서로 합치는 이미지를 갖고 있는데요, AggregateException은 그렇다면 여러 개의 예외를 하나로 합치는 예외타입이라는 말이 됩니다. 그런데, 여러 개의 예외는 어디서 오는 걸까요? 예전에 작성했던 예제를 조금 수정해서 확인 해보도록 하죠.

using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Threading.Tasks;

namespace Exam7
{
    class Program
    {
        static void Main(string[] args)
        {
            IEnumerable<string> files =
                Directory.GetFiles("C:\\음악", "*", SearchOption.AllDirectories);
            List<string> fileList = new List<string>();

            Console.WriteLine("파일 개수 : {0}", files.Count());
           
            Parallel.ForEach(files, (file) =>
            {
                FileInfo fileInfo = new FileInfo(file);
                if (fileInfo.Exists)
                {
                    if (fileInfo.Length >= 15000000)
                    {
                        throw new ApplicationException("15메가 넘는 파일이!!");
                    }
                    else if (fileInfo.Length >= 1000000)
                    {
                        fileList.Add(fileInfo.Name);
                    }
                    Console.Write("{0}", Task.CurrentId.ToString());                   
                }
            });
        }
    }
}

<코드3> 약간 수정된 예제.

<코드3>이 바로 그 예제인데요, 예제를 보면, 음악 폴더에서 15메가 넘는 파일이 발견되면, 예외를 발생하도록 되어있습니다. flac같은 파일로 보자면, 15메가 넘는 파일은 흔히 있겠지만, 저는 서민이라 mp3를 선호합니다. 어헣-_-. 아무튼, 이 예제를 실행 시켜보면요, 간혹 15메가 넘는 파일이 몇개는 있기 마련이기 때문에, 실행 중에 에러가 나게 되어 있습니다. 한번 디버깅을 해보죠.


<그림1> 병렬 스택 창

<그림1>의 병렬 스택을 보시면요, 병렬 ForEach문에 의해서 4개의 작업자 스레드가 생성된 걸 확인할 수 있습니다.(리스트의 크기나, CPU자원 상태등에 따라서 개수는 계속해서 변합니다.)

<그림2> 병렬 작업 창

그리고 <그림2>의 병렬 작업창을 보면, 2번 스레드가 15메가 넘는 파일을 만난 것을 확인할 수 있습니다. 그러면, 2번 스레드가 예외를 던질텐데, 나머지 작업은 어떻게 될까요? 처리 되지 않은 예외가 발생하는 순간, 다른 작업들은 모두 날아가버립니다.

그런데, 각 스레드 별로 파일 리스트를 쪼개서 줬을 텐데요. 각 스레드가 각자의 목록을 가지고 작업을 하다보면, 각 스레드 별로 15메가가 넘는 파일을 발견하게 될 것입니다. 이런 예외들을 만날 때 마다 처리하지 말고, 모두 모아서 한번에 처리하려면 어떻게 해야 할까요? 그래서 바로 AggregateException을 사용하는 거죠.

using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Threading.Tasks;
using System.Collections.Concurrent;

namespace Exam8
{
    class Program
    {
        static void Main(string[] args)
        {
            IEnumerable<string> files =
                Directory.GetFiles("C:\\음악", "*", SearchOption.AllDirectories);
            List<string> fileList = new List<string>();

            Console.WriteLine("파일 개수 : {0}", files.Count());

            var exceptions = new ConcurrentQueue<Exception>();

            try
            {
                Parallel.ForEach(files, (file) =>
                {
                    FileInfo fileInfo = new FileInfo(file);
                    if (fileInfo.Exists)
                    {
                        try
                        {
                            if (fileInfo.Length >= 15000000)
                            {
                                throw new ApplicationException("15메가 넘는 파일이!!");
                            }
                            else if (fileInfo.Length >= 1000000)
                            {
                                fileList.Add(fileInfo.Name);
                            }
                            Console.Write("{0}", Task.CurrentId.ToString());
                        }
                        catch (Exception ex)
                        {
                            exceptions.Enqueue(ex);
                        }
                    }
                });

                throw new AggregateException(exceptions);
            }
            catch (AggregateException ex)
            {
                foreach (var item in ex.InnerExceptions)
                {
                    Console.WriteLine("\n에러 : {0}", item.Message);
                }

                Console.Write("\n파일 리스트 계속해서 보기(엔터키를 치세요)");
                Console.ReadLine();
            }
            finally
            {
                foreach (string file in fileList)
                {
                    Console.WriteLine(file);
                }

                Console.WriteLine("총 파일 개수 : {0}", fileList.Count());
            }
        }
    }
}

<코드4> AggregateException을 사용.

ConcurrentQueue는 Queue긴 하지만, 여러 스레드에 의해서 동시에 큐에 추가를 하거나 해도 안전하도록 만들어진(thread-safe) Queue입니다. 그 큐에서 예외가 발생할 때마다, 예외를 저장해뒀다가, 한꺼번에 AggregateException으로 던지는 것이죠. 그리고 바깥쪽의 catch블록에서 예외를 받아서 내부의 예외 목록을 하나씩 처리하는 것입니다.

(생략)
에러 : 15메가 넘는 파일이!!

에러 : 15메가 넘는 파일이!!

에러 : 15메가 넘는 파일이!!

에러 : 15메가 넘는 파일이!!

에러 : 15메가 넘는 파일이!!

에러 : 15메가 넘는 파일이!!

에러 : 15메가 넘는 파일이!!

에러 : 15메가 넘는 파일이!!

에러 : 15메가 넘는 파일이!!

에러 : 15메가 넘는 파일이!!

에러 : 15메가 넘는 파일이!!

에러 : 15메가 넘는 파일이!!

에러 : 15메가 넘는 파일이!!

에러 : 15메가 넘는 파일이!!

파일 리스트 계속해서 보기(엔터키를 치세요)

<결과 3> 처리과정에서 생긴 예외를 모두 모아서 처리.


- 마무리.

오늘 예외처리에 대해서 봤습니다. 다음은~? 작업을 연쇄적으로 처리할 수 있도록 하는 부분을 보겠습니돠. 그럼 평안하시길. 어허허허허헣.


- 참고자료

1. Essential C# 4.0, Mark Michaelis, Addison Wesley
2. http://msdn.microsoft.com/en-us/library/dd460695.aspx
3. http://msdn.microsoft.com/en-us/magazine/ee321571.aspx
4. http://msdn.microsoft.com/en-us/library/system.aggregateexception.aspx

Hello Windows Azure / Understanding Windows Azure Development Process

Cloud 2010. 6. 7. 09:00 Posted by 알 수 없는 사용자

지난번 글 (http://www.vsts2010.net/313)에 이어서, 오늘은 Windows Azure 기반의 응용프로그램을 작성하는 과정에 대해서 실습하고 기본적인 이해를 더하는 내용을 살펴보도록 하겠습니다. 지난번 글에서 언급한 모든 구성 요소들이 설치되고, 관련된 패치들도 설치가 되어야 Windows Azure 기반의 응용프로그램 개발을 시작할 수 있음을 다시한번 말씀드립니다.

Windows Azure 개발 과정에서의 각 구성 요소들에 대한 이해

Windows Azure 위에서 호스팅될 응용프로그램을 개발하는 과정에서, 여러가지 구성 요소들이 필요합니다. 그리고 이러한 구성 요소들은 상당히 유기적으로 작용하게 되는데요, 겉으로 보기에는 복잡하지만 나름대로의 이유와 규칙들이 있습니다. 이번 장에서는 이러한 세부적인 내용들을 조명해보기로 하겠습니다.

우선, 지난번 글에서 언급한 Windows Azure Tools for Visual Studio의 역할을 살펴보겠습니다. Windows Azure Tools for Visual Studio는 이름에서 알 수 있듯이 Visual Studio와 연동하여 Visual Studio를 이용하여 손쉽게 응용프로그램을 개발하고 테스트할 수 있는 수단을 제공해줍니다. Visual Studio 2008과 Visual Studio 2010에 대한 연동 기능을 제공하고, 프로젝트 템플릿과 Visual Studio 확장 기능을 제공해주는 것이 주된 역할입니다. 그리고, Windows Azure SDK도 같이 설치해줍니다.

Windows Azure SDK는 실제 클라우드 컴퓨팅 환경과 최대한 비슷하게 기능을 재현하는 Emulation Service와 Windows Azure의 핵심 API, 클라우드 환경에서 응용프로그램 패키지를 쉽게 배포할 수 있도록 해주는 Package Builder를 제공합니다. Windows Azure SDK의 핵심 기능만 잘 이해하고, 활용할 수 있어도 Windows Azure 기반의 응용프로그램 개발은 이미 절반 이상 안 것이나 다름 없습니다.

실제로 실습을 하다보면 느끼시게 되겠지만 (특히 이전에 Windows Mobile이나 Windows Phone Series 7 기반의 개발 환경을 경험해보신 분들께서는 쉽게 이해할 수 있을 것입니다) Windows Azure의 개발 환경은 모바일 응용프로그램의 개발과 상당히 유사한 Perspective를 보여줍니다. 실제로 응용프로그램을 실행할 장치와 환경은 별도의 위치에 존재하지만, 그 이전까지 충분히 테스트를 해볼 수 있는 에뮬레이터와 모의 API 집합, 그리고 이에 관련된 문서들을 받아서 개발할 수 있다는 점이 비슷합니다. 다른 점이 있다면, 모바일이 아닌 전체 버전의 Windows Server를 기반 OS로 사용하는 응용프로그램을 개발한다는 점이 되겠습니다.

Windows Azure SDK를 이용하여 개발된 프로그램은 확장자가 CSPKG인 파일과 CSCFG인 파일로 최종 결과물이 나타납니다. CSPKG 파일은 몇 가지 기본적인 메타데이터와 함께 Windows Azure Fabric Controller를 통하여 배포할 수 있는 Binary Image가 담겨있는 (여기서의 Binary Image는 PE 파일 형식이 아닙니다. DLL의 형태도 아니고 EXE의 형태도 아닙니다.) ZIP 형식의 파일이고, CSCFG 파일은 XML의 형태를 띄고 있는 응용프로그램 전체에 대한 설정 파일입니다. 이 두 개의 파일을 Windows Azure Portal을 통하여 배포하게 되면 모든 처리 과정이 Windows Azure에 의해서 이루어지게되고 여러분의 서비스가 사용 가능한 상태로 진입하게 되는 것입니다.

Visual Web Developer 2010 Express로 Windows Azure 응용프로그램 개발 시작하기

앞서 설명드렸던 것처럼, Windows Azure 개발 환경을 Visual Studio와 함께 구축하셨을 경우에는 에뮬레이션 환경까지 같이 제공이 됩니다. 여기서 두 가지 선택을 할 수 있습니다.

에뮬레이션 환경에서 테스트하고 디버깅하는 과정을 거칠 필요가 있는 경우: 대부분의 경우 여기에 해당됩니다. 이 경우, 에뮬레이션 환경이 정상적으로 시스템에 설치되고 기동되기 위하여 Visual Studio 2008이나 Visual Studio 2010을 관리자 권한으로 "권한 상승"시킨 상태에서 시작해야 합니다.

간단하게 코드를 수정한 후 단순히 CSPKG, CSCFG 파일을 제작하기 위한 경우: 이 경우에는 관리자 권한으로 "권한 상승"한 상태에서 시작하지 않아도 무방합니다.

관리자 권한으로 권한 상승 시킨 상태에서 Visual Studio를 실행하는 경우, 일반적으로는 문제되지 않습니다. 하지만 다른 응용프로그램과 OLE 방식 (Drag & Drop과 같은 유형의 통신)으로 상호 작용하는 데에 문제가 있을 수 있습니다. 이러한 기능을 자주 사용하시거나, 프로그래밍하는 시간이 오래 걸리신다면 권한 상승을 하지 않고 먼저 완벽하게 프로그래밍을 끝낸 후 다시 관리자 권한으로 권한 상승시킨 후에 시작하여도 좋습니다.

관리자 권한으로 권한 상승하는 방법은 다음과 같습니다.

혹은, 관리자 권한 상승을 매번 사용하도록 고정시킬 수 있습니다. Windows Azure 기반 응용프로그램 개발을 집중적으로 활용하실 때에는 이 방법을 적용하시는 것도 좋습니다.

Note 1: 만약 Windows Azure Tools for Visual Studio를 설치하지 않은 상태에서 Cloud Project를 만들면?

기본으로 제공되는 개발 환경이 아니기 때문에 간혹 Visual Studio나 Visual Web Developer Express만 설치하고 Cloud Project를 생성하게 될 때가 있는데요, 걱정하지 않으셔도 됩니다. 아직 Tool과의 연동이 되지 않았다면 아래와 같이 Enable Windows Azure Tools 항목이 나타나며, 이를 이용하여 프로젝트를 생성하면 안내 웹 페이지를 포함하는 기본 문구가 나타납니다.

단순히 아래의 웹 페이지에서 Download Windows Azure Tools 버튼을 클릭하고, 다운로드 페이지에서 필요한 소프트웨어들을 모두 내려받아 설치하면 됩니다. (이 때, Visual Studio나 Visual Web Developer는 종료되어야 합니다.)

그리고 정상적으로 설치가 되었다면, 각 언어 별 (Visual C#, Visual Basic .NET, Visual F#)로 Cloud Category 아래에 Windows Azure Cloud Service 프로젝트 템플릿 항목이 보일 것입니다.

Note 2: 설치 시작과 동시에 혹시 아래와 같은 오류 메시지가 보이세요?


Installation Requirements:

These versions of Windows Azure SDK and Windows Azure Tools for Microsoft Visual Studio 2008 and Windows Azure Tools for Microsoft Visual Studio 2010 are already installed.  If the Windows Azure Cloud Service project templates are missing from Visual Studio, please uninstall the Windows Azure Tools and run this installer again.

내용인즉 그렇습니다. Windows Azure Tools는 일반적인 Windows Installer 기반의 응용프로그램이나 Visual Studio Installer 기반의 응용프로그램처럼 In-place Update가 (아마도 아직은) 지원되지 않습니다. 문제가 있어서 재설치하는 경우, 새 버전이 발표되어 재설치하는 경우, Visual Studio와 Visual Web Developer 중 어느 한쪽에 먼저 설치하고 나중에 추가 설치하게 되는 경우 모두 기존에 설치된 Windows Azure Tools를 먼저 제거한 후 다시 설치해야 합니다.

Windows Azure Tools로 프로젝트 만들어보기

이제 Windows Azure Tools를 이용하여 프로젝트를 시험삼아 만들어보겠습니다. Windows Azure Cloud Service 프로젝트 항목을 이용하여 프로젝트를 생성하면 아래의 화면과 같이 별도의 프로젝트 생성 마법사가 나타납니다. 여기서 필요한 만큼 Worker Role과 Web Role, 각 Role에서 사용할 언어, Role의 성격을 정의할 수 있습니다. Visual Studio 2008의 경우 사용 가능한 언어가 C#과 VB.NET으로 제한되고, Visual Studio 2010의 경우 C#과 VB.NET 외에 Worker Role에 한하여 F#도 사용할 수 있습니다.

추가하기를 원하는 프로젝트 템플릿의 종류를 좌측 Roles 목록에서 선택하고, ">" 버튼을 클릭하여 Cloud Service Solution 목록에 추가합니다. 여러 언어를 동시에 추가하고 관리할 수 있으며 필요하지 않을 것 같은 프로젝트는 우측에서 항목을 선택한 후 "<" 버튼을 클릭하여 다시 제거할 수 있습니다. 아래는 선택의 예시입니다. 일반적으로, 대표 Web Role 하나와 여러 개의 Sub Worker Role의 조합을 많이 사용합니다.

프로젝트의 이름을 지정하기 위하여, 우측 목록에서 이름을 바꾸기 원하는 항목을 선택하고, 마우스가 항목 위에 롤 오버 되었을 때 나타나는 우측의 연필 모양 아이콘을 클릭하면 이름을 바꿀 수 있는 입력란이 나타나게 됩니다. 이 때 원하는 이름을 입력하면 됩니다. 모든 설정이 아래와 같이 끝이 났다고 하였을 때 OK 버튼을 클릭하여 프로젝트를 생성합니다.

이제 아래와 같이 프로젝트가 생성되는 것을 보실 수 있습니다.

위의 그림에서 주황색 사각형으로 강조 표시한 파일들이 각각의 Role에서 핵심이 되는 파일들입니다. Worker Role은 백그라운드 작업을 중심으로 구성되는 프로그램이기 때문에 프로그램의 시작을 직접 프로그래밍 코드로 구현하는 것이 주가 되며, Web Role은 처음 보여줄 웹 페이지를 정의하는 일이 주가 되기 때문입니다. 그리고 우측의 솔루션 탐색기에서 파란색 사각형으로 강조 표시한 항목이 우리가 나중에 Windows Azure에 프로그램을 배포할 때 사용하는 핵심 단위 프로젝트입니다. 이 프로젝트를 정상적으로 표현하고 기능을 활용할 수 있기 위한 것이 Windows Azure Tools for Visual Studio의 주된 역할입니다.

다음 시간에는

다음 시간부터는 Windows Azure 기반의 응용프로그램에서 가장 고른 기능 사용 분포를 보여주는 트위터 스타일의 방명록 응용프로그램을 단계별로 작성하는 과정을 설명하도록 하겠습니다. Windows Azure Storage의 Queue, BLOB, Table을 사용하도록 구성되어있으며, SQL Azure를 이용하여 사용자 인증을 수행하기까지 하는 과정을 종합적으로 다루게 될 것입니다.

감사합니다. :-)

WCF Hosting - WAS를 이용한 Hosting

WCF 2010. 6. 7. 09:00 Posted by 알 수 없는 사용자

이제 포스팅 할 때마다 오랜만에 글 쓴다는 말하기도 미안해지네요,, 한 달만에 WCF에 관한 포스팅을 하게 되었습니다. 꾸준히 하겠다는 말을 하기도 민망하지만,, 어찌됐든, 이런 민망함을 뒤로하고 본론으로 들어가 보도록 하겠습니다. 

이번 포스팅의 주제는 Hosting 입니다.
호스팅은 WCF 서비스를 클라이언트에서 사용할 수 있게끔 해주는 중요한 작업이죠,, 이정도는 다들 알고 계실거라 생각합니다.

WCF는 여러 가지의 방법으로 호스팅을 할 수 있는 장점이 있습니다. 이는 제가 처음 WCF를 소개할 때도 언급했던 내용이었구요. 그래서, 지금부터는 WCF 서비스를 호스팅할 수 있는 방법과 호스팅할 때 사용할 수 있는 기능에 대해서 알아볼까 합니다.

호스팅에 대한 내용을 하나의 포스팅에 담기에는 내용이 조금 많은 것 같아서 두, 세번으로 나뉘어 포스팅 하도록 하겠습니다. (이렇게 말해놓고 다음 포스팅이 또 한달을 넘긴다면,, 다들 욕(?)하시겠죠, ^^;;)

지금까지 8번의 포스팅을 진행하면서 제가 예제로 사용했던 WCF 서비스들은 모두 Self Hosting이었습니다.

"응?? Self Hosting 이라뇨? 그건 뭔가요? 먹는건가요?"

라고,, 궁금해 하실 수도 있을 것 같습니다.
Selft Hosting이란,, 특별한 무언가가 있는 것은 아니구요, System.ServiceModel 네임스페이스에 정의 되어있는 ServiceHost 클래스를 사용하여 직접 서비스를 호스팅한 것을 의미합니다.

아시겠죠? 지금까지 제가 사용했던 예제들은 모두 콘솔 어플리케이션 내에서 직접 ServiceHost 클래스를 이용하여 서비스를 호스팅했으니 모두 Self Hosting이 되는 것입니다.
물론, 윈도우 폼 프로그램, WPF 프로그램 그리고, 윈도우 서비스를 이용하여 WCF 서비스를 호스팅할 수 있는데, 이것들 역시 셀프 호스팅(Self Hosting) 인 것입니다.

그럼, 셀프 호스팅을 제외하고 어떤 호스팅 방법이 있을까요?
WCF 서비스를 한번이라도 개발하신 분이라면 아마 사용했을 법한 IIS 호스팅이 있습니다. 그리고 IIS7에서 사용할 수 있는 WAS 호스팅이 있습니다.

IIS 호스팅은 웹 서비스 처럼 IIS를 이용한 호스팅이라 특별한 것도 없고, WCF 서비스를 호스팅하기 위한 가장 쉬운 방법이기도 하구요, 그래서 따로 포스팅을 하지 않아도 될 것 같습니다.

그래서, 이번 포스팅은 WAS 호스팅에 대해서 좀 더 자세히 알아보려 합니다.

WAS(Windows Process Activation Service) 를 이용한 호스팅

WAS는 Windows Process Activation Service의 약자로 IIS 7.0의 기본 구성 요소로서 HTTP 이외의 프로토콜(TCP, MSMQ, Named Pipes)을 사용한 서비스를 호스팅할 수 있게 해주는 역할을 수행합니다.
WAS에 대한 자세한 내용을 알고 싶다면 다음 링크를 참조 하시기 바랍니다. (WAS로 HTTP를 초월한 WCF 서비스 확장)

그럼, 간단하게 WAS가 어떻게 HTTP 이외의 프로토콜을 사용할 수 있게 하는지 알아볼까요?
다음은 WAS 의 아키텍처를 간략하게 표현한 그림입니다.


서버는 서버로 어떤 요청이 들어왔을 때, 그 요청을 처리할 수 있는 수신기를 가지고 있으며, 이 수신기(Listener)는 각 요청의 프로토콜에 맞는 수신기 어댑터(TCP 수신기 어댑터, MSMQ 수신기 어댑터, Named Pipe 수신기 어댑터)로 요청을 보냅니다. 수신기와 WAS 사이에는 수신기 어댑터 인터페이스가 존재하는데, 이를 이용해 각 프로토콜의 수신기 어댑터는 전달받은 요청을 WAS로 보낼 수 있게 되는 거죠. 그리고 WAS 에서는 각 요청에 맞는 응용 프로그램 인스턴스를 생성하기 위해 작업자 프로세스로 요청을 전달하게 되는 것입니다.

이러한 일련의 작업들을 통해 WAS는 HTTP 이외의 프로토콜을 이용한 서비스를 가능하게 하는 것입니다.
설명이 조금 복잡한 것 같지만, 대충 어떻게 돌아가는지는 아시겠죠? ^^;;

그럼, WAS를 이용하여 호스팅을 해보도록 하겠습니다.

WAS는 Vista 이상의 Windows 에서는 IIS를 설치할 때 기본으로 함께 설치가 됩니다. 이 때는 HTTP 수신기 어댑터가 설치 되구요, .NET 3.5 이상의 버전이 설치될 때 TCP, MSMQ, named pipe 수신기 어댑터가 설치 됩니다.

WAS를 이용한 서비스를 만들기 위해 가장 먼저 해야할 것은 IIS를 이용한 호스팅을 하는 WCF 서비스 솔루션을 만드는 것입니다. WAS를 이용한 서비스를 만드는 특별한 방법이 있는 것은 아니며, 기본적으로 WAS는 IIS 7.0의 요소이기 때문에 우선 IIS를 이용한 서비스를 만드는 것이 가장 먼저 해야할 일인 것입니다.

Visual Studio 2010에서 다음 그림처럼 "WCF 서비스 응용 프로그램" 솔루션을 선택합니다.


이렇게 새로운 솔루션을 생성하면, IIS를 이용한 WCF 서비스를 만들 수 있습니다. 기본적으로 Service1.svc 라는 파일이 만들어지는데 이 파일이 WCF 서비스의 로직이 담기는 핵심 파일입니다.(솔루션의 구조는 다음 그림과 같은 모습을 하고 있습니다.)


여기서 서비스의 기능을 바꾸려면 어떤 파일을 수정해야 할까요? 바로 찾을 수 있으시겠죠? IService1.cs 와 Service1.svc.cs 파일을 바꾸어야 합니다. 물론, 이번 포스팅에서의 주제는 WAS를 이용한 호스팅이기 때문에 자동으로 만들어지는 서비스의 로직을 굳이 바꿀 필요는 없을 것 같습니다.

그래~서~!! 저는 따로 서비스의 기능을 바꾸지 않고 자동으로 만들어진 서비스를 그대로 이용해 보겠습니다. 절대 귀찮아서 그러는 것은 아닙니다!! (응? ;;)

솔루션을 만든 후 바로 Ctrl+F5 키를 눌러서 실행을 시켜보도록 하죠~ ㅎ

ASP.NET 솔루션을 실행시킨 것 처럼 ASP.NET Development Server가 시작되고, 웹 브라우저를 이용해 svc 파일을 탐색해 보면 다음과 같이 WCF 서비스 안내 페이지를 만날 수 있을 것입니다.


IIS를 이용한 호스팅은,,. 정말 너무나도 쉽습니다. ServiceHost 클래스를 사용하지도 않고, 자동으로 만들어진 파일 그대로 실행을 해도 무리없이 실행이 되는 것을 알 수 있습니다.

오늘 포스팅의 목적인 WAS를 이용한 호스팅을 구현하기 위해 몇가지 설정을 해보도록 하겠습니다.
바로 이전에 실행했던 서비스는 당연한 이야기겠지만, IIS를 이용한 호스팅이었고, 이는 HTTP 프로토콜을 사용하고 있습니다. 

WAS 호스팅을 실행하기 위해서 우선 서비스 솔루션을 ASP.NET Development Server 가 아닌 IIS에서 실행이 되도록 바꾸어야 합니다. 그래서 솔루션 속성창 "웹" 메뉴에서 "IIS 웹 서버 사용"으로 설정해줍니다. (아래 그림 참조)


IIS 관리자를 실행시키고, 이 사이트가 돌아갈 수 있도록 새로운 사이트를 만들어 주는 것도 잊으시면 안됩니다~ 이 내용은 다들 알고 계시리라 생각하고 생략하겠습니다 ^^

WAS를 이용한 호스팅의 목적은 HTTP 프로토콜 이외의 다른 프로토콜을 사용하기 위함입니다. 그래서 저는 이번 예제에서 TCP 프로토콜을 사용할 수 있는 서비스를 만들어보려 합니다. TCP 프로토콜을 사용할 수 있도록 하기 위해선 IIS 관리자에서 설정을 변경해주어야 할 것이 몇 개 있습니다.

IIS 관리자에서 해당하는 사이트를 선택하고 오른쪽에 위치한 "고급설정"을 클릭하여 설정 창을 띄웁니다. 고급설정 창에서 "사용할 수 있는 프로토콜"에 아래 그림과 같이 "net.tcp"를 추가하여 줍니다.



그리고, 한 가지 더 설정해주어야 할 것이 있는데, 해당 사이트와 특정 프로토콜에 대한 바인딩입니다. 사이트 작업 메뉴에서 "바인딩"을 클릭하여 사이트 바인딩 창을 띄웁니다. 그리고, 추가 버튼을 클릭하여 다음 그림과 같이 net.tcp 프로토콜에 대한 바인딩 정보를 입력하여 줍니다.


여기까지 설정이 완료되면, WAS를 이용하여 호스팅을 하기 위한 IIS 사이트 설정이 모두 끝이 납니다. 어렵지 않죠? ^^

이제, 마지막으로 앞에서 만든 WCF 서비스에 TCP 프로토콜을 사용할 수 있도록 netTcpBinding을 사용하는 엔드포인트를 추가시켜주어야 합니다. WCF 서비스 솔루션에 있는 web.config 파일을 열어보죠.

Visual Studio 2010을 이용해 서비스를 만드셨다면, web.config의 내용이 조금 달라졌다고 느끼실 것 같습니다. 사실, 내용이 많이 달라진 것은 아니고, 그 전 버전에 비해 web.config의 내용이 아주 많이 간략화되었습니다. 매우 매우 심플해졌죠. 이 내용에 대해서 나중에 따로 포스팅을 할 기회가 있을 것이라 생각됩니다.

엔드 포인트를 다음과 같이 포함시켜 보겠습니다.

<system.serviceModel>

    <services>

      <service name="WAS_Hosting.Service1">

        <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />

        <endpoint binding="netTcpBinding" contract="WAS_Hosting.IService1" />

      </service>

    </services>
    ...
</system.serviceModel>


service 태그의 name 속성과 endpoint 태그의 contract 속성은 WCF 서비스의 네임스페이스와 클래스 이름을 잘 확인하여 설정해주셔야 합니다.

IMetadataExchange를 컨트랙트로 사용하는 엔드포인트도 같이 설정을 해주는 걸 볼 수 있습니다. 이는 서비스의 메타 데이터에 접근할 수 있는 엔드 포인트란 것을 알 수 있으실 겁니다. 이에 대한 내용은 다음 포스팅을 참조해 주시면 될 것 같습니다. (첫 WCF 서비스 만들기 2)

자~ 이제 모든 설정이 끝났습니다. 여기까지 따라오시느라 수고하셨습니다~ ^^

이 서비스가 제대로 서비스가 되는지 확인해보기 위해 콘솔 프로젝트를 새로 만들고 서비스 참조를 통해 우리가 만들었던 WCF 서비스를 참조해보도록 하겠습니다.

IIS 관리자에서 저는 net.tcp에 대한 바이딩을 할 때 포트번호를 8081로 설정했었습니다. 따라서 그림에서 볼 수 있듯이 "net.tcp://localhost:8081/Service1.svc" 주소로 WCF 서비스에 접근할 수 있습니다.


서비스를 참조한 후에 서비스를 사용하는 방법은 특별한 것이 없습니다. 예전에 셀프 호스팅 서비스를 사용했던 것과 같은 방법으로 사용하면 되기에 클라이언트 쪽 코드는 생략하고, 결과 화면만 첨부하겠습니다.


WCF 서비스가 아주 잘 동작하는 것을 확인할 수 있습니다. 유후~ ^^

이번 포스팅의 내용은 여기까지 입니다.
쓸데없이 내용이 길어진 것 같다는 생각도 들지만, 어쨌든 여기까지 읽어 주셔서 너무 감사드리구요~
다음번 포스팅때 다시 뵙도록 하겠습니다. ^^

[STL] 1. What's new in VC++ 2010?

C++0x 2010. 6. 7. 00:00 Posted by 알 수 없는 사용자

소개
안녕하세요. 이번에 팀블로그에 합류하게 된 방수철이라고 합니다.
저는 VC++ 2010에서 구현된 Standard C++ Library에 대해서 소개를 드리고자 합니다.
이 글에서는 간단하게 어떤 변화가 있었는지 개요를 설명드리고, 이 후의 글에서 하나씩 집어보려 합니다.


Standard C++ Library의 변경사항

  • STL의 많은 함수에서 move semantics와 perfect fowarding을 구현하기 위해서 rvalue reference를 사용하였습니다. Move semantics와 perfect fowarding은 변수나 매개변수의 메모리 할당이나 변수 할당의 성능을 크게 향상시킵니다.

  • auto_ptr 클래스보다 더 안전한 스마트 포인터형인 unique_ptr 클래스의 구현에도 Rvalue reference가 사용되었습니다. unique_ptr 클래스는 move는 할 수 있지만 copy는 불가능하며, safety에 영향을 미치지 않으면서 강한 소유 의미(strict ownership semantics)를 구현했습니다. 또한, unique_ptr 클래스는 rvalue references가 구현된 container들과 잘 동작합니다.
  • <memory>헤더에 포함된, 새로운 make_shared 함수는 어떤 객체가 만들어지는 동시에 shared pointer를 만들기에 편리하고, 안전하며, 효율적입니다.

  • find_if_not, copy_if, is_sorted 와 같은 15개의 새로운 함수가 <algorithm>에 추가되었습니다.

  • 단반향으로 연결된 리스트(single linked list)가 <forward_list> 헤더에 의해서 지원됩니다.

  • cbegin, cend, crbegin, crend 멤버 함수는 container에서 앞으로 또는 뒤로 움직이는 const_iterator를 제공합니다.

  • <system_error> 헤더와 관련된 템플릿은 저수준 시스템 오류를 처리하는 것을 지원합니다.

  • exception_ptr 클래스의 멤버들은 쓰레드 간에 예외를 전송하기 위해 사용될 수 있습니다.

  • <codecvt> 헤더는 유니코드 문자의 다양한 인코딩을 다른 인코딩으로 변환하는 것을 지원합니다.

  • <allocators> 헤더는 node기반의 container에 대한 메모리 블럭의 할당과 해제를 돕는 여러 템플릿을 정의합니다.

  • <random> 헤더에 많은 업데이트가 있습니다.

다음 글들에서는 각각에 대해서 좀 더 자세히 소개하도록 하겠습니다.

'C++0x' 카테고리의 다른 글

[STL] 3. unique_ptr (2/2)  (0) 2010.08.04
[STL] 2. unique_ptr (1/2)  (0) 2010.07.27
[Plus C++0x] 람다(Lambda) 이야기 (마지막회)  (2) 2010.06.03
[Plus C++0x] 람다(Lambda) 이야기 (3)  (0) 2010.06.01
[Plus C++0x] 람다(Lambda) 이야기 (2)  (1) 2010.05.27

Asynchronous Agents Library - agent. 1 ( 상태 )

VC++ 10 Concurrency Runtime 2010. 6. 5. 08:00 Posted by 알 수 없는 사용자

Asynchronous Agents Library – agent. 1 ( 상태 )

작성자: 임준환( mumbi at daum dot net )

 

Agent 란 무엇인가

개념

 Agent 는 사전적 의미로 동작이나 행위를 행하는 사람이나 사물이라는 뜻을 가지고 있습니다. 그 말 그대로 Asynchronous Agents Library( 이하 AAL ) 의 agent 는 어떤 행위를 하는 객체를 나타냅니다.

 지난 글에서 AAL 이 actor-based programming 이라고 언급한 적이 있습니다. 여기에서 actor 가 바로 agent 를 일컫습니다.

 이해를 돕기 위해 비유를 하자면 agent 는 하나의 작업이라고 생각해도 좋습니다. 그런데 이 작업은 단순히 한 번 처리되는 작업을 말하는 것이 아니라, 어떤 책임이나 역할을 하는 작업입니다. 우리는 멀티 스레드 프로그래밍을 할 때, 이런 개념을 worker 라고 말하기도 합니다.

 예를 들어, 네트워크 프로그래밍을 할 때, 소켓에 연결 요청을 기다리고 요청이 들어오면 연결해주는 역할을 하는 스레드가 필요합니다. 이 스레드는 어떤 책임이나 역할을 하는 작업을 가지고 있습니다. 이 스레드를 객체로 표현하면 agent 라고 할 수 있습니다.

클래스

 실제 개발 시 필요한 agent 클래스는 Concurrency 네임스페이스 안에 존재하고, agents.h 파일 안에 정의되어 있습니다.

 agent 클래스는 추상 클래스로 agent 로 만들 클래스가 상속해야 합니다. agent 클래스의 추상 메소드는 void run() 이고, 반드시 구현해야 합니다.

- 예

#include <iostream>
#include <agents.h>

using namespace std;
using namespace Concurrency;

class TestAgent
	: public agent
{
protected:
	void run()
	{
		wcout << L"running.." << endl;
		this->done();
	}
};

[ 코드1. agent 상속 예 ]

 

agent 의 상태

 agent 는 비 동기 처리를 목적으로 하기 때문에, 내부적으로 스레드를 사용할 수 밖에 없습니다. agent 의 상태란 agent 의 작업을 처리하는 스레드의 상태라고 볼 수 있습니다.

 agent 는 생명 주기를 갖는데, 이 주기는 상태로 표현됩니다.

[ 그림1. agent 샐명 주기 ]

[ 그림1. agent 생명 주기 ]


 처음 agent 상태는 크게 초기 상태, 활성화 상태, 종료 상태로 나눌 수 있습니다. agent 객체를 생성하면 그 agent 는 초기 상태인 created 상태가 됩니다. start() 를 호출하면 Concurrency Runtime 에 의해 해당 agent 가 스케줄 되고 runnable 상태가 됩니다.

 createdrunnable 상태에서 cancel() 을 호출하여 작업을 취소하면 canceled 상태가 되는데 이는 종료 상태 중 하나입니다.

 위의 그림에서 점선으로 표시된 run() 은 우리가 명시적으로 호출하는 것이 아니라 runnable 상태인 agent 를 Concurrency Runtime 이 호출하는 것을 의미합니다. run() 이 호출되면 활성화 상태인 started 상태가 됩니다.

 만약 모든 작업의 수행이 완료되었으면 done() 함수를 호출하여 종료 상태인 done 상태로 바꾸어야 합니다. 동기화를 위한 wait() 등의 함수는 해당 agent 가 종료 상태 즉, done 이나 canceled 상태가 되어야 반환됩니다.

 agent 생명 주기에서 가장 중요한 것은 순환되지 않는다는 것입니다. 즉, 되돌아 갈 수 없습니다. 한 번 종료 상태를 갖은 agent 객체는 이미 쓸모 없는 객체가 되고 재사용할 수 없습니다. 그러므로 해당 작업을 다시 수행하기 위해서는 새로운 agent 객체를 생성해야 합니다.

 다음은 각 상태에 대한 정의입니다.

 agent 상태  설명
 agent_created  agent 가 아직 스케줄 되지 않음.
 agent_runnable  Concurrency Runtime 이 agent 를 스케줄 하고 있음.
 agent_started  agent 가 실행 중임.
 agent_done  agent 의 작업이 완료됨.
 agent_canceled  agent 가 실행 되기 전에 취소됨.

[ 표1. agent status ]

 이 상태들은 status() 메소드로 알아 낼 수 있습니다. status() 는 동기화되기 때문에 정확한 상태를 반환하지만, 그 상태가 현재의 상태라고 보장할 수 없습니다. 왜냐하면 status() 가 반환된 직 후 agent 의 상태가 바뀔 수 있기 때문입니다.

- 예

코드

#include <iostream>
#include <agents.h>

using namespace std;
using namespace Concurrency;

class TestAgent
	: public agent
{
protected:
	void run()
	{
		wcout << L"running.." << endl;
		this->done();
	}
};

void print_agent_status( agent& a )
{
	wstring status;
	switch( a.status() )
	{
	case agent_status::agent_created:	status = L"agent_created";	break;
	case agent_status::agent_runnable:	status = L"agent_runnable";	break;
	case agent_status::agent_started:	status = L"agent_started";	break;
	case agent_status::agent_done:		status = L"agent_done";		break;
	case agent_status::agent_canceled:	status = L"agent_canceled";	break;
	}

	wcout << status.c_str() << endl;
}

int main()
{
	TestAgent testAgent;

	print_agent_status( testAgent );

	testAgent.start();

	for( int i = 0; i < 10; ++i )
	{
		print_agent_status( testAgent );
	}

	agent::wait( &testAgent );

	print_agent_status( testAgent );
}

[ 코드2. agent 생명 주기 ]

실행 결과 

[ 그림2. 코드2 실행 결과 ]

[ 그림2. 코드2 실행 결과 ]

 

마치는 글

 이번 글에서는 agent 의 개념과 클래스, 그리고 상태에 대해서 알아보았습니다. agent 를 사용하기 위해서는 조금 더 알아야 할 것들이 있습니다.

 조금 더 알아야 할 내용들은 다음 글에 작성해 보도록 하겠습니다.

 

참고

  • 그림1. agent 생명 주기 - http://i.msdn.microsoft.com/dynimg/IC338844.png

지난 6월 1일 Microsoft 의 여러가지 제품과 기술이 총 집합하는 행사 REMIX10 입니다. 이날 Visual Studio 2010 공식 팀 블로그는 여러 파트너사 부스와 Visual Studio 2010 제품의 메인 부스에 버금가는 규모로 부스가 운영되었습니다.    

이 날, 클라우드, 모바일, VS2010 등의 큰 테마를 주제로 진행된 다양한 세션 중 단연, Visual Studio 2010 이 참석하신 여러분의 기술적인 욕구를 충분히 충족할 수 있는 시간이 되지 않았나 생각합니다.    

필자는 Visual Studio 2010 이 주는 새로운 기능에 단지 포커스를 맞추지 않습니다. 왜냐하면 기능은 언제든지 보강을 할 수 있기 때문이죠. 그것보다 더 중요한 것은 Visual Studio 2010 이 우리에게 주는 가치입니다. 제가 생각하는 가치관 중에 "가치는 가치를 아는 자만이 가치를 안다"라는 것처럼 Visual Studio 2010 의 가치를 말이죠.    

왜 Visual Studio 2010 이여야만 하는가…?    

Visual Studio 2010 의 장점이라면 바로 통합 환경(IDE-Integrated Development Environment) 를 제공합니다. 하지만 지금의 패러다임은 단순 통합 환경은 더 이상 매력이 없습니다. 왜냐하면 어떤 다른 개발 플랫폼이든 통합 환경을 제공하고 있기 때문입니다.    

감히 이제는 Visual Studio 2010 을 통합 개발 환경이라고 부르지 마십시오. 왜냐하면 Visual Studio 2010 은 라이프 사이클 관리 도구(Lifecycle Management) 입니다. 단순한 개발 영역 뿐만 아니라, 소프트웨어 개발을 위한 착수, 설계, 테스트, 폐기, 운영에 아우르는 모든 일련의 과정이 Visual Studio 2010 으로 모든 것이 가능해집니다.    

또한 기존의 딱딱한 개발 환경은 잊어버리셔도 좋습니다. 어느 누구에게도 자신의 스타일이 있듯이 기존의 개발 도구도 개발 도구만의 스타일이 있었습니다. 하지만 Visual Studio 2010 은 개개인의 스타일에 맞추어 드립니다. 자신에게 착~ 맞는 옷이 가장 뽀대가 나듯이, 자신에게 가장 잘 맞는 개발 도구가 최상의 개발 생산성을 낼 수 있는 것과 같은 이치 입니다.    

   

개발자끼리의 소통 수단은 단지 "코드" 입니까? 코드를 잘 만드는 사람이 고급 개발자이고, 코드를 잘 만들지 못하는 사람이 초급 개발자인 시대는 이미 몇 년 전 이야기 입니다. 코드는 비즈니스 영역의 산출물이지, 코드가 목적이 되어서는 안됩니다.    

코드는 비즈니스 목표를 해석해 놓은 산출물입니다. 결국 코드는 어플리케이션이 잘 동작하도록 설계대로 구현된 산출물입니다. 코드를 어떻게 구현했느냐는 2차적인 수단이며, 코드의 흐름을 파악할 수 있는 것이 더욱 중요합니다.    

Visual Studio 2010 의 강력한 데이터 시각화 기능들은 코드 중심이 아닌, 전체적인 흐름을 파악할 수 있는 통찰력을 제공해 줍니다.    

   

소프트웨어의 버그는 왜 발생하는가….? 필자의 블로그의 [ALM-Test] 왜 단위 테스트를 해야 하는가? [2] 에서 언급했듯이 코드, 클래스, 컴포넌트, 레이어간의 연동 관계가 생성되면서 주로 발생하게 됩니다. 코드의 자체적인 결함 보다는 연동/종속적인 관계가 형성이 되면서 버그는 증식이 됩니다.    

최근 우리나라의 소프트웨어 업계에서 테스트라는 영역은 이전보다 더 큰 관심을 보이고 있습니다. 테스트는 어떻게든 할 수 있지만, 어떻게 가시적이고 올바른 테스트가 가능한 것인가에 대해서 말입니다. Visual Studio 2010 과 Team Foundation Server 2010 그리고 Test & Lab 은 테스트에 대한 방법론을 가장 올바르게 가이드하는 환경을 제공해 줍니다.    

상상해 보셨습니까? 테스트가 생산 공정의 라인처럼 자동화 된다는 것을… 이미 그 상상은 현실이랍니다. ^^    

   

   

Visual Studio 2010 에 대한 문의 사항은 메일 powerumc at gmail.com 또는 트위터 @powerumc, @vsts2010 으로 언제든지 연락을 주시기 바랍니다. 가장 좋은 방법으로 도구와 시스템을 업그레이드 하는 방법이나 기술 업체와 연결하여 드리겠습니다.

Just for fun! / Visual Studio Express Edition

Visual Studio 2010 2010. 6. 4. 09:00 Posted by 알 수 없는 사용자

새로운 버전의 Visual Studio가 출시되었지만, 설치하는데 시간도 오래 걸리고 새로운 기능을 일일이 테스트해보기 어려우신 분들이 많으실 것입니다. 혹은, Imagine Cup과 같은 IT 경진 대회를 준비하시는 학생 여러분들은 적당한 소프트웨어 개발 도구를 찾을 수 없어 고민이 크실 수 있습니다. 오늘 강좌에서는 이러한 고민을 덜어줄 가볍고 편리한 Visual Studio의 새 버전을 소개해드릴까 합니다.

그 주인공은 바로 Express Edition 입니다. Visual Studio Express Edition은 지난 Visual Studio 2005부터 지속적으로 업데이트되고 있는, 이제는 Microsoft Visual Studio Family의 중요 멤버로 자리잡은 주요 제품군 중 하나가 되었습니다. Express Edition이라는 이름에서 알 수 있듯이, 개발자들에게 가장 핵심적인 Subset을 제공하는 버전입니다. 하지만 그 기능은 "절대 빈약하지 않습니다."

Visual Studio Express Editon은 Visual Studio .NET 2002/2003 제품군이 발표될 무렵까지 유지되어왔던 개별 언어 제품군들에 대한 새로운 묶음 패키지입니다. Visual Basic .NET, Visual C#, Visual C++, Visual Web Developer를 주축으로 하며, 여기에 부수적으로 SQL Server의 Express Edition까지 항상 같이 제공되어왔었습니다. Visual Studio 2005 시절에는, 지금은 지원이 중단되었지만 Visual J#에 대한 Express Edition도 제공되었던 적이 있었습니다.

왜 Express Edition인가?

Visual Studio Express Edition은 단지 공짜로 제공되는 프리웨어이기 때문에 가치가 있는 것은 아닙니다. Microsoft가 발표하는 주요 최신 기술들을 재빨리 시험해볼 수 있는 훌륭한 플랫폼으로 Express Edition의 역할이 점차 옮겨가고 있으며, 대표적인 예가 바로 Visual Studio Express Edition for Windows Phone 7입니다. 그리고 XNA Framework 역시 Express Edition을 기초로 모든 SDK를 제공해왔었습니다.

저 개인적으로는 Express Edition을 매우 애용합니다. Express Edition은 프로토 타이핑 프로그래밍을 할 수 있도록 여러분의 Mental Model을 바꾸어줍니다. Visual Studio를 이용하여 프로그래밍을 시작하는 것은, 사용하는 시스템의 종류에 따라서는 또 하나의 새로운 운영 체제를 로딩하는것과 같은 기분을 느끼게 하여 부담스러울 수 있습니다. 하지만, 전체 기능을 로드하지 않고서 필요한 프로그래밍 언어에 해당되는 IDE만을 로드하기 때문에 훨씬 로딩 속도도 빠릅니다. 그리고, C#과 VB.NET의 경우, 프로젝트를 임시 디렉터리에 생성하는 것을 기본으로 하기 때문에, 여러분의 프로젝트 디렉터리가 지저분해질 걱정을 하지 않고 작은 코드와 프로그램을 시험해보기 위하여 망설임없이 IDE를 켤 수 있습니다. 만약 프로젝트를 실제로 작성하기를 원치 않는다면, 프로젝트를 닫을 때 나타나는 저장 여부 대화 상자에서 단순히 "버리기" 버튼만 클릭하면 됩니다.

LINQPad (http://www.linqpad.net/), Small Basic (http://www.microsoft.com/downloads/details.aspx?FamilyID=61481b74-eb45-42b8-a777-8f3644406787&DisplayLang=ko)과 같은 유틸리티와 다른점을 언급해본다면, 역시 이러한 유틸리티들은 IDE가 아닌, 간단한 도구의 수준 안에 있습니다. 하지만 이러한 도구들로는 충족하기 힘든, 그러나 정식으로 프로젝트를 만들고 코드를 Organizing하는데에 노력이 필요하지 않은 정도의 임시 코드를 작성하고 편안하게 테스트할 수 있는 환경을 찾으신다면 그것이 Express Edition의 역할이라 할 수 있겠습니다.

Express Edition에 관한 몇 가지 중요한 질문과 답변들

아래의 질문 목록들은 Express Edition을 선택하고 활용하는 데에 몇 가지 중요한 기준들이 될 수 있습니다.

A. 불법 소프트웨어 단속에 걸리지 않는 완전한 프리웨어인가? 

예. 다만, 30일 이내에 Windows Live ID를 사용하여 완료할 수 있는 무료 등록 절차를 통하여 Serial Number를 받아 프로그램에 등록해야 합니다. 30일 이후에도 등록되지 않은 상태로 남아있으면 프로그램 시작 시 등록을 하지 않으면 실행되지 않도록 프로그램이 변경됩니다.

B. Express Edition을 이용하여 상용 소프트웨어를 개발할 수 있는가?

예. 또한, 오픈 소스 라이선스 (GPL, LGPL, MPL, BSD 등)를 대상으로 하는 소프트웨어 프로젝트에서도 Express Edition을 사용하는데에 아무런 제약이 없습니다.

C. Crystal Report나 Microsoft Report Designer가 제공되는가?

아니오. 제공되지 않습니다.

D. Visual C++ Express Edition의 경우 MFC나 ATL 프로젝트를 생성하고 GUI를 디자인할 수 있는가?

아니오. MFC나 ATL 프로젝트를 새로 생성하는 기능이 없고, GUI 디자인도 제약이 있습니다. 단, Visual Studio 2010 Professional 이상에서 작성한 Visual C++ Project를 Express Edition에서 기본적인 수준에서 열거나 편집하거나 빌드하는 것에는 환경 설정이 정확하다면 문제가 없습니다.

E. Express Edition을 이용하여 Silverlight Project나 Windows Azure Application을 만들 수 있는가?

예. Silverlight Tools와 Windows Azure Tools 모두 Visual Web Developer의 기능 집합을 이용하여 SDK가 설치되므로 Visual Web Developer Express Edition을 설치하면 Silverlight와 Windows Azure 응용프로그램을 개발할 수 있습니다.

F. Windows Installer 프로젝트를 생성할 수 있는가?

아니오. 다만 ClickOnce 기능은 Visual Basic .NET Express Edition과 Visual C# Express Edition 모두 지원됩니다.

G. 여러가지 서로 다른 종류의 프로젝트를 한 솔루션 안에 포함할 수 있는가?

아니오. 교차 언어 지원은 Express Edition 이상의 버전 (예: Standard / Professional)에서부터 지원됩니다. 다만, 같은 언어끼리 한 솔루션 안에 여러 프로젝트를 생성하고 관리하는 것에는 문제가 없습니다.

Visual Studio 2010 Express Product Feature Chart

아래의 차트를 확인하시면 Visual Studio 2010 Express Edition이 실제로 지원하는 기능들에 대하여 좀 더 직관적으로 이해하실 수 있습니다. (출처: http://www.microsoft.com/express/Downloads/#2010-Visual-Web-Developer)

Visual Studio Express Edition 설치하기

Visual Studio Express Edition을 설치하려면 http://www.microsoft.com/express 에 방문한 뒤 Download 링크를 클릭하여 원하는 제품을 선택하여 웹 설치 마법사를 다운로드하거나, 전체 버전의 DVD ISO 이미지 파일을 다운로드하여 ISO 마운트 프로그램을 이용하여 설치 프로그램을 오프라인에서 실행하는 방법이 있을 수 있습니다. 이 글을 작성하는 현 시점에서 Express Edition에도 한국어 버전이 추가되어, Visual Studio 2010과 .NET Framework 4.0의 최신 기능을 빠르고 간편하게 테스트해보실 수 있습니다.

각각의 제품을 설치하신 뒤에는 30일 이내에 제품 등록을 하셔야 합니다. 제품 등록 이전에는 "평가판"이라는 문구가 나타나지만 등록을 한 이후에는 정품으로 변환되기 때문에 걱정하지 않으셔도 됩니다. 등록하는 과정에서 Windows Live ID가 필요합니다.

Express Edition 화면 미리 보기

01234

마지막으로 제안 한 가지

Internet Explorer 6.0을 사용하지 말자는 제안은 정말 널리 퍼져있고, 이제는 어느정도 Internet Explorer 6.0이 왜 문제가 되는지 충분히 공감대가 형성되어있습니다. 하지만, Visual C++ 컴파일러는 어떤가요? 아직도 6.0을 사용하고 계신가요?

서비스 팩 6를 설치했다고 할지라도 Visual C++ 6.0은 Internet Explorer 6.0보다 훨씬 더 오래된 제품입니다. http://en.wikipedia.org/wiki/Visual_C%2B%2B 페이지의 설명에 따르면 Visual C++ 6.0은 1998년에 발표된 제품이고, 이 글을 작성하는 현 시점에서 생각해보면 무려 12년이나 된 제품입니다. http://archvista.net/1328 에서 소개하는 것 처럼, 아직도 Visual C++ 6.0을 사용하신다면 여러분은 12년 묵은 우유를 드시는것과 다름이 없습니다.

이 글을 보시게 될 수도 있을 전산학부 교수님, 조교님, 그리고 학생 여러분들께 부탁드립니다. Visual C++ 6.0에 대한 라이선스를 가지고 있다는 이유만으로, 바꾸기 귀찮다는 이유만으로 과제를 검사하고 테스트하지 말아주십시오. 10년이면 강산도 바뀌는 세월입니다. Visual C++ 컴파일러도 업그레이드가 필요합니다. 전체 버전을 이용하지 않더라도, Visual Studio 2008 또는 2010 Express Edition이 충분한 솔루션이 될 수 있습니다.

긴 글 읽어주셔서 감사합니다. 즐거운 하루 되세요. :-)

[Step 02-1] 클래스(class), 핸들(^), 그리고 구조체(struct)

C++/CLI 2010. 6. 4. 08:30 Posted by 알 수 없는 사용자

C C++의 큰 차이점의 하나가 바로 C++에만 있는 클래스입니다. 또 클래스는 객체 지향 프로그래밍 언어에서 자주 볼 수 있습니다.

 

C++/CLI에서 관리 클래스는 ‘ref’라는 키워드를 사용하여 만듭니다.

 

비 관리 클래스

class Test

{

};

 

관리 클래스

ref class Test

{

};

 

관리 클래스는 ‘ref’를 제외하고는 외관상으로는 비 관리 클래스와 비슷하지만 관리와 비 관리가 많이 다르듯이 관리 클래스는 비 관리 클래스와 다른점이 있습니다. 그러므로 이것을 잘 파악하고 있어야 합니다.

 

 

 

관리 클래스의 특징

 

1. 정적 할당은 안 된다. 무조건 가비지컬렉션(GC)에 동적으로 생성한다.

 

2. 복사 생성자를 만들 수 없다.

 

3. ‘^’(핸들이라고 부른다)‘gcnew’를 사용하여 클래스를 생성한다. 당근 메모리 해제는 GC에서 관리한다.

 

4. 핸들은 네이티브의 ‘*’(포인터) ‘&’(참조)와 비슷한 것으로 더 안정스럽다.

 

5. ‘delete’는 명시적으로 클래스에서 사용하고 있는 리소스를 해제할 때 사용하는 것으로 소멸자가 호출 되는 것이지 메모리에서 해제하는 것은 아니다. delete GC에 있는 메모리를 해제하는 것은 아니다.

 

6. delete로 소멸자를 호출하면 GC에 의해서 진짜 파괴될 때 소멸자는 호출되지 않는다.

(소멸자를 선언한 클래스는 자동적으로 IDisposable 인터페이스를 구현한다. 소멸자는 컴파일러에 의해서 Dispose() 메소드로 치환된다).

 

7. 소멸자 이외에 'finalize'를 선언할 수 있다. finalize GC에서 인스턴스가 파괴될 때 호출된다. delete로 소멸자를 호출한 경우에는 finalize는 호출되지 않는다.

 

8. finalize‘!’ 키워드를 사용한다.

 

 

 

관리 클래스 사용해 보기

 

< 코드 1. 관리 클래스 정의 및 사용 >

#include "stdafx.h"

 

using namespace System;

 

ref class ManagedTest

{

public:

           ManagedTest() { Console::WriteLine(L"New ManagedTest"); }

           ~ManagedTest() { Console::WriteLine(L"delete ManagedTest"); }

           !ManagedTest() { Console::WriteLine(L"finalize ManagedTest"); }

};

 

int main(array<System::String ^> ^args)

{

           Console::WriteLine(L"1.");

          ManagedTest^ MTest1 = gcnew ManagedTest();

           delete MTest1;

 

           Console::WriteLine(L"2.");

           ManagedTest^ MTest2 = gcnew ManagedTest();

          

           return 0;

}

 

< 결과 >





<코드 1> 설명



< 그림 1. <코드 1>의 소스 코드와 결과 >

 

<그림 1> 코드를 보면 3번의 ‘gcnew’, 4번의 ‘^’를 사용하여 ManagedTest를 생성하였습니다.

<코드 1>에는 없지만 핸들은 포인터와 같은 것으로 관리 클래스의 멤버를 사용할 때는 ‘->’를 사용합니다.

5 delete를 사용하면 소멸자가 호출되어서 6번이 출력됩니다.

MTest2는 소멸자를 호출하지 않아서 8번에서 정의한 finalize가 프로그램이 종료할 때 호출됩니다(7).


 

<코드 1>은 아주 짧고 단순한 코드이지만 관리 클래스의 대부분의 특징을 다 나타내고 있습니다. 관리 클래스에 대한 설명은 아직 남아 있습니다. 이것은 다음 회에 또 설명하겠습니다.