Visual Studio 2010 공식 팀 블로그 @vsts2010

Posted by 임준환( 멈비 )

Asynchronous Agents Library
– message block 4. ( call )

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

 

시작하는 글

 이번 글에서는 call 이라는 message block 에 대해서 알아보겠습니다. 이번에 알아볼 call 또한 이미 알아본 message block 들과는 다른 특징이 있으니 특징을 이해하고, 필요할 때 바로 사용할 수 있어야 합니다.

 

call< _Type >

 call 은 함수 포인터와 같은 역할을 합니다. call 에 message 를 보내면 그 message 는 생성자에서 지정한 함수 포인터의 인자로 사용됩니다. 그래서 call 에 message 를 보낸다는 것은 함수를 호출하는 것과 같은 효과를 얻을 수 있습니다.

 

특징

 call 과 함수 호출의 다른 점이 있습니다. call 에 의해서 수행되는 함수를 동기 또는 비 동기로 수행되도록 선택할 수 있습니다.

 동기 전달 함수인 send() 를 사용하여 message 를 전달하면 지정된 함수의 수행이 종료될 때까지 기다립니다. 그렇기 때문에 지정된 함수의 수행이 종료되어야 해당 context 가 진행됩니다.

 반면에 비 동기 전달 함수인 asend() 를 사용하여 message 를 전달하면 지정된 함수의 수행이 종료될 때까지 기다리지 않고 호출한 context 는 계속 진행됩니다. 즉, 호출한 스레드와 call 의 함수가 동시에 진행됩니다.

 이처럼 동기와 비 동기 처리를 적절히 선택하여 구현할 수 있습니다.

 또 하나 기억해 두어야 할 특징은 하나의 call 객체에 message 를 여러 차례 보내더라도 동시에 수행되지 않습니다. 하나의 call 객체는 내부적으로 작업 큐( queue ) 를 가지고 있어 순차적으로 수행됩니다.

 하지만 하나의 call 객체가 아니라 서로 다른 call 객체는 asend() 를 이용해 동시에 수행될 수 있습니다. 서로 각각의 작업 큐를 가지고 있기 때문입니다.

 call 의 안타까운 점은 함수의 반환을 처리하지 못한다는 점입니다. 만약 지정된 함수의 결과를 얻어내야 한다면 해당 함수 내부에서 다른 message block 으로 결과를 message 로 보내야 합니다.

 call 은 생성자 이외에 public 인 멤버 함수가 없습니다.

 

선언

 사실 call 이라는 message block 의 타입은

template < class _Type, class _FunctorType = std::tr1::function< void( _Type const & ) > >
class call

입니다.

 tr1 의 function 은 일반 함수 포인터와 멤버 함수 포인터, 함수 객체를 모두 가질 수 있으므로 기본 템플릿 매개변수인 _FunctorType 을 지정하지 않고 사용하는 것이 좋습니다. 

 위의 선언이 보여주듯이 _Type 은 수행될 함수의 매개변수의 타입입니다. bind 와 같은 어댑터 함수들과 함께 사용하여 여러 인자들을 바인딩할 수 있습니다.

 

예제

 call 의 특징들을 쉽게 알아볼 수 있는 예제를 구현해보았습니다.

 

시나리오

 Loader 라는 agent 클래스가 작업을 진행하고 call 을 이용해 진행 상황을 화면에 출력해주는 예제입니다.

 

코드

#include 
#include 

using namespace std;
using namespace Concurrency;

class Loader
	: public agent
{
public:
	Loader( call< unsigned int >& displayDelegate )
		: displayDelegate( displayDelegate ) { }

protected:
	void run()
	{
		for( unsigned int percent = 0; percent <= 100; percent += 10 )
		{
			Concurrency::wait( 1000 );
			asend( this->displayDelegate, percent );
		}

		this->done();
	}

private:
	call< unsigned int >&		displayDelegate;
};

int main()
{
	call< unsigned int >	showLoading( []( unsigned int percent )
	{
		switch( percent )
		{
		case 0:		wcout << L"loading..";					break;
		case 100:	wcout << endl << L"complete!" << endl;	break;
		default:	wcout << L"..";							break;
		}		
	} );

	Loader loader( showLoading );

	loader.start();

	agent::wait( &loader );
}

[ 코드1. call 의 비 동기 처리를 이용한 예제 ]

 Loader agent 의 작업 진행 상황을 화면에 출력합니다. 진행률이 0 일 때는 “loading.." 을 출력하고, 이후, 진행이 완료되기 전까지 “..” 을 추가하여 진행 상황을 알리고, 완료되면 “complete!” 를 출력하여, 작업의 완료를 알립니다.

 

[ 그림1. call 의 비 동기 처리를 이용한 예제 ]

[ 그림1. call 의 비 동기 처리를 이용한 예제 ]

 

마치는 글

 이미 알아본 message block 들과는 많이 다른 call 에 대해서 알아보았습니다. call 은 함수의 수행을 동기 및 비 동기 처리로 할 수 있도록 도와주는 반면 결과를 얻기는 쉽지 않습니다.

 이러한 call 의 단점의 보완과 더 많은 특징을 가지고 있는 transformer 라는 message block 을 제공합니다.

 다음 글에서는 transformer 를 이용해 더욱 더 신나는 멀티 코어 프로그래밍을 해보도록 해보겠습니다.

크리에이티브 커먼즈 라이선스
Creative Commons License

댓글을 달아 주세요

Posted by 오태겸(RuAA)
여름의 정점을 지나가고 있는 듯 합니다. 날씨가 무진장 덥네요,,
장마가 끝나면 폭염이라는데,, ㅡ,.ㅡ;;;
식상한 멘트이긴 하겠지만, 이런 날일 수록 정말 건강이 중요한 것 같습니다.
덥다고 에어컨 바람만 쐬면 냉방병 걸리고, 밤에 잘때도 너무 에어컨 틀어놓으면 감기 걸리니깐,,
아프지 않는게 최고죠~ 특히, 저 같이 혼자 자취하는 자취남들은,, ^^
아무쪼록 건강하세요~


이번 아티클의 주제는 Windows Service를 이용한 호스팅입니다.

윈도우즈 서비스에 대해서 모르는 분이 계실까요?
네~ 계실 수 있죠,, 그래서 간략하게 윈도우즈 서비스에 대해 설명을 하고 넘어가도록 하겠습니다.

What is Windows Service?

NT 서비스라고 알려져 있기도 한 윈도우즈 서비스는 자체의 Windows 세션에서 실행되며, 윈도우즈가 구동을 하고 있는 동안 계속 동작을 해야하는 이러한 작업이 필요할 때, 윈도우즈 서비스의 형태로 구현하여 사용할 수 있습니다. 또한, 윈도우즈 서비스는 윈도우즈의 시작과 함께 자동으로 시작할 수 있기 때문에, 서버가 동작함과 동시에 항상 동작해야하는 응용 프로그램이라면 윈도우즈 서비스로 구현하는 것을 고려해 볼 필요가 있을 듯 합니다.

윈도우즈 서비스의 경우는 따로 UI를 가지고 있지 않기 때문에, 윈도우즈 사용자 또는 관리자가 윈도우즈 서비스의 구동 상태를 상세하게 확인할 수는 없습니다. 다만, 윈도우즈에 기본으로 제공되는 서비스 제어 관리자를 통해 서비스의 시작, 중지, 일시정지 등을 컨트롤 할 수 있습니다. (아래 그림은 서비스 제어 관리자의 모습입니다.)


그림을 보니 윈도우즈 서비스가 어떠한 것들을 말하는 것인지 알겠죠? ^^ (역시, 백문이 불여일견,, 엣헴~)

Windows 서비스 응용프로그램에 대한 소개와 개발 방법에 대해 좀 더 자세한 정보를 원하시는 분은 다음 링크를 참고하시기 바랍니다.

"Windows 서비스 응용 프로그램 소개"

이제 본격적으로 이 윈도우즈 서비스를 이용하여 WCF 서비스를 호스팅하는 방법에 대해 적어보겠습니다.
고고씽~

Windows Service를 이용한 WCF 서비스 호스팅

WCF 서비스를 호스팅하는 방법에는 IIS 호스팅, WAS 호스팅, 그리고 셀프 호스팅으로 나뉘어진다고 얘기했었습니다.

그럼, Windows Service를 이용한 호스팅은 어디에 속할까요?
네~!! 당연히 셀프 호스팅에 속합니다. 그리고, 셀프 호스팅에 속한다는 말은 직접 ServiceHost 클래스를 이용하여 호스팅을 구현 해야 한다는 말이기도 합니다.

결국, 여기서 제가 하고 싶은 말은 이것입니다.
 "Windows Service를 이용하여 WCF 서비스를 호스팅하기 위해서는 ServiceHost 클래스를 이용하여 서비스 호스팅하는 부분을 직접 구현해야 한다."
 
WCF 서비스를 호스팅하기 위한 특별한 코드가 필요한 것은 아닙니다. 여타의 다른 윈도우즈 서비스를 개발하는 것과 같이 개발을 하고, WCF 서비스를 호스팅하는 코드만 추가를 해주면 된다는 것입니다~!!
아마, 윈도우즈 서비스를 한번이라도 개발 해 보신 분은 어렵지 않게 개발을 할 수 있을 것 같습니다.

우선, 윈도우즈 서비스 응용프로그램을 만들기 위해 Visual Studio에서 새 프로젝트를 생성할 때, "Windows 서비스" 템플릿을 선택합니다.


프로젝트를 생성하면 Service1.cs 와 Program.cs 파일이 생성되어 있음을 확인할 수 있습니다.
Program.cs 파일은 이 응용 프로그램의 진입점을 가지고 있으며, 실제 서비스가 동작을 할 때의 코드는 Service1.cs 파일에 구현을 하면 됩니다.

그리고, Service1.cs 파일을 확인하면 Service1 클래스가 선언되어 있으며, 이 클래스는 ServiceBase 클래스를 상속 받는 것을 확인할 수 있습니다.
그리고, Service1 클래스에는 기본적으로 OnStart와 OnStop 메소드가 재 정의(override)되어 있는데 메소드의 이름으로 짐작할 수 있겠지만, 각각 서비스가 시작할 때 와 서비스가 멈췄을 때의 동작을 수행하는 메소드입니다.

이 메소드 외에, OnContinue, OnPause, OnShutdown 메소드를 재정의하여 서비스를 일시정지에서 다시 시작했을 때, 서비스를 일시정지 했을 때, 그리고 컴퓨터가 종료될 때의 동작을 구현할 수 있습니다.

이번 아티클의 주제는 Windows 서비스를 이용한 WCF 서비스의 호스팅이니 만큼, Windows 서비스 구현에 대한 자세한 내용은 앞서 걸어놓은 MSDN의 링크로 대신하고 넘어가겠습니다.
(Windows 서비스 응용 프로그램의 구현에 대한 자세한 내용을 모르셔도 아래 내용을 따라 하시면, 아마 무사히 WCF 서비스를 호스팅 할 수 있으실겁니다. )

음,, 일단 우리가 만들 Windows 서비스의 이름을 먼저 변경해보도록 하겠습니다. 이름을 변경하지 않아도 별 상관은 없지만 우리가 만든 서비스란 것을 알아보기 위해서 변경하는게 낫겠죠~ 서비스의 이름을 변경하는 것은 어렵지 않습니다. Service1 클래스의 생성자에서 바꿔줘도 되고, Service1.Designer.cs 파일을 확인하면 Service1 의 partial 클래스가 정의되어 있는데, 이곳에 위치한 InitializeComponent 메소드 내에서 변경해줘도 됩니다. 저는 InitializeComponent 메소드 내에서 다음과 같은 코드로 서비스의 이름을 지정 해주었습니다.

private void InitializeComponent()

{

     components = new System.ComponentModel.Container();

  this.ServiceName = "RuAA WCF Service"; // 서비스의 이름 변경

}


이제 본격적으로 WCF 서비스를 호스팅 해보도록 하겠습니다.
우선, WCF 서비스에서 사용할 ServiceContract와 DataContract 들을 선언해주어야 겠죠. 다음과 같은 인터페이스와 클래스들을 선언해주었습니다.

// ServiceContract 정의
[
ServiceContract]

public interface IProductService

{

    [OperationContract]

    Product GetProductInfo(int id);

}


// DataContract 정의
[
DataContract]

public class Product

{

    [DataMember]

    public int ID;

    [DataMember]

    public string Name;

    [DataMember]

    public string Company;

    [DataMember]

    public int Price;

}

// 서비스 구현

public class ProductService : IProductService

{

    List<Product> productList;

 

    public ProductService()

    {

        productList = new List<Product>();

        productList.Add(new Product {

            ID = 1,

            Name = "ABC Chocolate",

            Company = "RuAA Inc.",

            Price = 5300

        });

    }

 

    public Product GetProductInfo(int id)

    {

        var item = (from p in productList

                    where p.ID == id

                    select p).FirstOrDefault();

 

        return item;

    }

}

항상 그랬지만, 서비스의 역할은 심플합니다. 복잡한 서비스를 만드는게 목표는 아니잖아요~ ㅎ

이제 이렇게 구현된 서비스를 호스팅하는 일 만을 남겨두었습니다.

앞에서도 밝혔듯이 Windows 서비스에서 WCF 서비스를 호스팅 하는 것은 Self Hosting 에 포함되는 것이기 때문에 ServiceHost 클래스를 직접 구현해야 합니다.

근데 이 코드를 어디에 위치해야 할까요? 예상하신 분들이 분명 계실겁니다.
바로 바로 바로~~!! Service1 클래스의 OnStart 메소드입니다. 

또한, 생각해야 할 것이 있습니다.
Windows 서비스가 멈추었을 때, 당연히 WCF 서비스의 호스팅을 멈추어줘야 겠죠. 그래서 OnStop 메소드 내부에 WCF 서비스의 호스팅을 멈추게 하는 코드도 포함이 되어야 할 것입니다.

Service1 클래스를 다음과 같은 코드로 구현해보았습니다.

// ServiceHost 클래스의 인스턴스를 클래스의 멤버로 선언하여 Service1 클래스의 메소드에서 접근이 가능하도록 한다.
private
ServiceHost svcHost;

// 윈도우즈 서비스가 시작할 때의 동작을 구현한다.

protected
override void OnStart(string[] args)

{

    // ServiceHost 인스턴스 생성
    string baseUrl = "http://10.30.101.84:9090/ProductService";

    this.svcHost = new ServiceHost(typeof(ProductService), new Uri(baseUrl));

    // 엔드포인트 추가
   
this.svcHost.AddServiceEndpoint(typeof(IProductService),

        new BasicHttpBinding(),

        "");

           

    // 메타 데이터 엔드포인트를 위한 Behavior 설정
    ServiceMetadataBehavior metaBehavior = new ServiceMetadataBehavior();

    metaBehavior.HttpGetEnabled = true;

    svcHost.Description.Behaviors.Add(metaBehavior);

 

    // 메타 데이터 엔드포인트 추가

    this.svcHost.AddServiceEndpoint(typeof(IMetadataExchange),

        MetadataExchangeBindings.CreateMexHttpBinding(),

        "mex");

 

    svcHost.Open();  // WCF 서비스 오픈

    ServiceEndpoint endpoint = this.svcHost.Description.Endpoints[0];

 

    // 윈도우즈 이벤트 로그에 정보를 남긴다.

    EventLog.WriteEntry(endpoint.Contract.Name + " Started"

        + " listening on " + endpoint.Address

        + " (" + endpoint.Binding.Name + ")",

        System.Diagnostics.EventLogEntryType.Information);

}

 

// 윈도우즈 서비스가 멈췄을 때의 동작을 구현한다.

protected override void OnStop()

{

    this.svcHost.Close();

    EventLog.WriteEntry("RuAA Service Stopping", EventLogEntryType.Information);

}
 
위의 코드에서 그렇게 어려운 점은 보이지 않습니다. Self Hosting을 해보셨던 분이라면 말이죠~
혹시나, Self Hosting을 해보지 못하신 분이 있다면, 첫 WCF 만들기 아티클을 참고해주시기 바랍니다.

각 주요 코드에 주석도 남겨놨으니 따로 긴 설명은 필요없을 듯 합니다. ^^

이제, 서비스의 구현은 모두 끝이 났습니다. 하지만, 우리가 만든 이 Windows 서비스를 컴퓨터에 설치하기 위해서는 설치 관리자가 필요합니다. (설치 관리자에 대한 자세한 설명은 이곳으로~)
Service1.cs 파일의 디자이너 보기에서 마우스 우측 클릭한 후 나타나는 메뉴에서 "설치 관리자 추가"를 선택합니다.


그러면 프로젝트에 ProjectInstaller.cs 라는 파일이 생기는데, 이 클래스의 디자인 뷰를 확인하면,  serviceProcessInstaller1, serviceInstaller1 이라는 이름의 컨트롤들이 포함되어 있는 것을 확인할 수 있습니다.

이 컨트롤들의 속성을 다음 그림과 같이 수정해보겠습니다.



이제서야, 정말 Windows 서비스를 설치할 모든 준비가 끝이 났습니다.
이 Windows 서비스를 설치하기 위하여 Visual Studio 명령 프롬프트를 실행시킵니다. 이때, 관리자 권한으로 실행시켜 주셔야 합니다. 그렇지 않으면 Windows 서비스가 제대로 설치가 되지 않는 경우가 있더라구요~ ㅎ

그리고, 이 프로젝트 폴더의 bin/Debug 폴더로 이동한 후에 다음 그림과 같이, Installutil 이란 명령어를 이용하여 우리가 만든 Windows 서비스를 설치합니다. (Windows 서비스 설치에 대한 자세한 설명은 이곳으로~!!)
참고로, 설치 파일은 프로젝트를 빌드한 후에 생성된 exe 파일입니다.


위의 그림처럼 "트랜잭트 설치가 완료되었습니다." 란 메세지가 떨어졌다면 아무 에러없이 Windows 서비스가 설치되었다는 말입니다. 그럼, 서비스 제어 관리자에서 확인해 보도록 하겠습니다.


네,, Windows 서비스가 올라와있는 것을 볼 수 있네요~ 그럼 시작 버튼을 클릭하여 Windows 서비스를 시작할 수 있습니다. 그리고, Windows 서비스가 시작하면서 우리가 구현한 WCF 서비스가 호스팅되겠죠,, ㅎㅎ

그럼, 이 WCF 서비스가 제대로 호스팅 되고 있는 것인지 확인을 해보아야 할겁니다,,
역시나, 지금까지 그래왔듯이 콘솔 어플리케이션을 이용하여 확인해보도록 하겠습니다.

솔루션에 콘솔 어플리케이션 프로젝트를 추가하고, 이 프로젝트에 서비스 참조를 시켜주었습니다. 이때 너무나도 당연하겠지만, 서비스의 주소는 ServiceHost 인스턴스에 추가 시켜준 엔드 포인트의 주소를 넣어주셔야 합니다.ㅎ


위의 그림처럼, 서비스를 제대로 찾으면~ 모든 것이 OK!!! 입니다. ㅎㅎ

이 서비스를 이용한 콘솔 어플리케이션 코드는 생략해도 되겠죠?? ㅎㅎ (이번 글이 너무 길어진 것 같아,, ^^;;;;)
그래서~~~ 결과 화면만 보여드리겠습니다 ㅎㅎ



후아~ 결과가 잘 나오는 군요,, ^^
이로써, 이번 포스팅도 무사히(?) 끝을 낼 수 있게 되었습니다. ㅎㅎㅎ

이번 포스팅은 Windows 서비스에 대한 설명과 구현에 대한 내용을 함께 적다보니 조금 길어진 것 같습니다. 물론 한번이라도 Windows 서비스 응용 프로그램을 구현해보신 분이라면 아는 내용들이겠지만, 혹시나 모르시는 분도 있을 것 같아 자세한 내용까지 설명드리지 못했지만 최소한의 내용을 포함시켰습니다.

조금 내용이 길어졌지만 끝까지 읽어주신 분들께 심심한 감사의 인사를 드리며, 저는 이만 퇴근(?)하겠습니다 ㅎㅎ
저작자 표시 비영리 변경 금지
크리에이티브 커먼즈 라이선스
Creative Commons License

댓글을 달아 주세요

  1. Windows NT 서비스를 이용하여 접목할 수 있는 실제 사례가 인상 깊네요. 강좌 잘 읽었습니다. :-)

Posted by 정홍주

 

 

이전 내용에서 REST 에 대한 소개와 .NET에서 REST API를 이용해서 SharePoint 데이터를 액세스하는 내용을 알아보았습니다. 이번 내용은 Silverlight 응용 프로그램에서 REST API를 통해 액세스하는 방법을 다루어봅니다.

 

먼저 Silverlight 응용 프로그램을 생성합니다. Silverlight 디자인은 아래와 같이 Button ListBox로 구성합니다.

 

Silverlight 응용 프로그램에 ListData.svc를 서비스 참조 추가를 통해 프록시 클래스를 생성합니다.

프록시 클래스의 클래스 뷰를 보면 앞에서 살펴본 .NET 응용프로그램과 동일하다는 것을 알 수 있습니다.

 



Button
의 클릭 이벤트를 생성하고 아래와 같은 코드를 작성합니다. HomeDataContext 클래스의 인스턴스를 생성하고 DataServiceQuery 클래스를 통해 비동기적으로 호출합니다.



GetAsyncData 메서드에서 결과값을 바인딩하면 됩니다.


 

Xap 파일을 배포해서 SharePoint 2010 Silverlight 웹 파트에 연결해서 버튼을 클릭하면 손쉽게 Silverlight 응용프로그램에서도 SharePoint 데이터를 REST 를 통해 액세스할 수 있다는 것을 알 수 있습니다.

 

크리에이티브 커먼즈 라이선스
Creative Commons License

댓글을 달아 주세요