'2019/01'에 해당되는 글 3건

Programming/.NET

속도가 느린 입출력장치와의 통신이나 혹은 다른 메서드를 호출했을때 처리가 완료되기를 기다리지 않고 현재 스레드에서 곧장 제어를 넘겨받아 작업을 계속진행하는 방식을 비동기 호출(asynchronous call)이라고 합니다.

 

class Program
{
    delegate void callMethod();

    static void Main(string[] args)
    {
        callMethod cm = new callMethod(myMethod);
        IAsyncResult ar = cm.BeginInvoke(null, null);

        Console.WriteLine("현재 스레드 완료");

        cm.EndInvoke(ar);

        Console.Read();
    }

    static void myMethod()
    {
        Console.WriteLine("메서드 실행");

        Thread.Sleep(10000);

        Console.WriteLine("완료");
    }
}

 

위 예제는 메서드의 실행을 호출하되 비동기로 호출하는 방식을 보여주고 있습니다. 우선 메서드의 형식에 대한 델리게이트(delegate)를 선언하고 해당 델리게이트의 BeginInvoke로 메서드를 호출합니다.

 

EndInvoke는 메서드에서 반환값을 받는 경우에 필요하지만 그렇지 않다면 생략이 가능합니다. 다만 반환값이 없더라도 호출해줄것을 권장합니다.

 

BeginInvoke가 호출되면 CPU는 스레드풀에서 대기중인 스레드를 불러와 메서드의 실행을 맡김으로서 비동기를 실현합니다. 그리고 메서드가 종료되면 해당 스레드는 스레드의 상태를 나타내느 상태값을 Signal로 바꾸게 되는데 BeginInvoke에서 반환하는 IAsyncResult타입의 AsyncWaitHandle속성이 스레드의 상태를 대기하는 EventWaitHandle과 같으므로 필요시 이 속성을 사용해 스레드의 실행을 동기화할 수 있습니다.

 

static void Main(string[] args)
{
    callMethod cm = new callMethod(myMethod);
    IAsyncResult ar = cm.BeginInvoke(null, null);
    ar.AsyncWaitHandle.WaitOne();

    Console.WriteLine("현재 스레드 완료");

    cm.EndInvoke(ar);

    Console.Read();
}

 

위와 같이 메서드를 비동기로 호출할때 만약 메서드에 특정 값을 매개변수로 전달해야 한다면 다음과 같이 처리할 수 있습니다.

 

class Program
{
    delegate int callMethod(int i, int j);

    static void Main(string[] args)
    {
        callMethod cm = new callMethod(myMethod);
        IAsyncResult ar = cm.BeginInvoke(10, 20, null, null);

        Console.WriteLine("현재 스레드 완료");

        int sum = cm.EndInvoke(ar);

        Console.WriteLine("결과 : " + sum);
        Console.Read();
    }

    static int myMethod(int i, int j)
    {
        Thread.Sleep(10000);

        return i + j;
    }
}

 

myMethod에서는 2개의 정수값을 받아 이를 합한 결과를 반환하도록 하였습니다. 그에 따라 델리게이트(delegate)도 같은 형식으로 수정하고 BeginInvoke호출시 필요한 매개변수를 추가해줍니다. 결과값은 이전에 말씀드린대로 EndInvoke를 통해 받을 수 있습니다.

 

비동기 메서드 호출이후 별도의 메서드를 호출하는 콜백메서드가 필요하다면 원하는 메서드를 정의하고 BeginInvoke의 세번째 매개변수에 해당 메서드를 지정해주기만 하면 됩니다.

 

class Program
{
    delegate int callMethod(int i, int j);

    static void Main(string[] args)
    {
        callMethod cm = new callMethod(myMethod);
        IAsyncResult ar = cm.BeginInvoke(10, 20, methodComplete, null);

        Console.WriteLine("현재 스레드 완료");

        int sum = cm.EndInvoke(ar);

        Console.WriteLine("결과 : " + sum);
        Console.Read();
    }

    static int myMethod(int i, int j)
    {
        Thread.Sleep(10000);

        return i + j;
    }

    static void methodComplete(IAsyncResult ar)
    {
        Console.WriteLine("완료");
    }
}

 

지금까지는 임의의 사용자 메서드를 어떻게 비동기로 호출하는지를 알아봤는데 .NET의 몇몇클래스에도 비동기로 호출할 수 있는 장치가 마련되어 있습니다. 예를 들어 파일을 스트림(Stream)으로 읽어들이는 FileStream클래스의 경우

 

static void Main(string[] args)
{
    using (FileStream fs = new FileStream("C:\\sampe.txt", FileMode.Open, FileAccess.Read)) {
        byte[] b = new byte[fs.Length];
        fs.Read(b, 0, b.Length);

        string s = Encoding.UTF8.GetString(b);
    }

    Console.Read();
}

 

일반적으로 위와 같이 사용할 수 있지만 파일이 너무 크거나 하는등의 이유로 파일의 내용을 다 읽을때까지 대기할 수 없다면 아래와 같이 비동기로의 호출이 가능합니다.

 

class Program
{
    delegate int callMethod(int i, int j);

    static void Main(string[] args)
    {
        FileStream fs = new FileStream("C:\\sample.txt", FileMode.Open, FileAccess.Read);
        byte[] b = new byte[fs.Length];
        fs.BeginRead(b, 0, b.Length, readComplete, b);

        Console.WriteLine("주 스레드 완료");
        Console.Read();
    }
    static void readComplete(IAsyncResult ar)
    {
        byte[] b = (byte[])ar.AsyncState;
        string s = Encoding.UTF8.GetString(b);
        Console.WriteLine(s);
    }
}

 

대부분의 클래스에서 비동기를 지원하는 경우 메서드는 Begin-- 으로 시작합니다. FileStream도 마찬가지 인데 Read메서드의 비동기 호출이 BeginRead이며 BeginRead 메서드를 호출할때 동작 완료시 사용될 메서드를 지정합니다.

'Programming > .NET' 카테고리의 다른 글

[C#] ? / Nullable  (0) 2019.02.12
[C#] partial  (0) 2019.02.07
[C#] 비동기 호출(asynchronous call)  (0) 2019.01.23
Debug / Release  (0) 2019.01.15
[ASP.NET MVC] Razor  (0) 2019.01.08
[ASP.NET MVC] URL 다루기  (0) 2018.12.18
0 0
Programming/.NET

프로그램을 개발할때 오류가 발생하면 대부분 Visual Studio의 디버거 프로그램을 통해 문제가 되는 코드를 찾아 수정하는 방법으로 해결이 가능합니다.

 

static void Main(string[] args)
{
    int[] i = new int[] { 0, 1, 2, 3, 4 };
    i[5] = 0;
}

 

이 프로그램은 컴파일시에는 정상적으로 처리되지만 정작 실행할때는 오류가 발생될것입니다. 그러나 다음과 같이 오류가 발생되는 정확한 소스코드 라인을 알려주기에 곧장 문제되는 부분을 찾아 수정하는 것이 가능합니다.

 

 

빌드하고 난 이후의 실행파일을 실행했을 뿐인데 이러한 소스코드상의 정보를 출력해 디버깅이 가능하도록 할 수 있는 이유는 프로젝트 자체를 Debug 모드로 빌드했기 때문입니다. 프로그램을 Debug모드로 빌드하게 되면 컴파일러는 소스코드를 전혀 건드리지 않으면서 소스코드관련 정보를 가지고 있는 PDB(program database)파일을 생성하게 되고, 문제가 발생했을 경우 CLR이 PDB파일을 로드하여 소스코드의 정보를 알려주는 것입니다.

 

그렇다면 소스코드를 건드리지 않는다는건 무슨 뜻일까요?

 

CLR은 프로그램이 실행될때의 성능에 맞게 소스코드 자체에 최적화를 적용하게 되는데 이는 빌드되는 과정에서 '최적화'라는 단계의 작업에 해당합니다. 예를 들어

 

static void Main(string[] args)
{
    int[] i = new int[] { 0, 1, 2, 3, 4 };
    SetArray(i);
}

static void SetArray(int[] i)
{
     i[5] = 0;
}

 

위와 같은 소스코드는 최적화를 거치면 아래와 같이 바뀔것입니다.

 

static void Main(string[] args)
{
    int[] i = new int[] { 0, 1, 2, 3, 4 };
    i[5] = 0;
}

 

이렇게 되는 이유는 굳이 메서드로 분리된 코드를 유지하면 매개변수로 값을 전달하고 해당 메서드를 호출해야 하는 등의 과정이 필요하기 때문입니다. 따라서 성능상 차라리 메서드의 내용을 호출자에 통째로 넣어버리는 것이 더 빠르기 때문에 위와 같은 결과가 발생하는 것입니다.(물론 모든 상황에서 이러한 동작이 수행된다고는 보장할 수 없지만) 결국 코드의 내용이 달라져 버린다는 것이 핵심이고 이런 처리를 수행하는 것은 프로젝트의 빌드모드를 Release로 했을경우에 해당합니다.

 

프로그램을 개발하는 개발단계에서 Release로 빌드하지 않는 이유는 바로 이 때문입니다. 최적화과정에서 소스코드를 변경해 버리면 디버거는 엉뚱한 소스코드의 라인을 알려주며 오류정보를 전달할 수 있기 때문입니다. 설령 PDB파일이 있다고 하더라도 PDB는 최적화 이전의 소스코드정보를 갖고 있기 때문에 별 도움이 되지 않습니다.

 

좀 다른 얘기지만 프로그램을 만들때 특정 조건의 실행여부를 판단하기 위해 종종 Console이나 혹은 MessageBox 클래스를 통해 메세지를 표시하곤 하는데 화면에 원하는 내용을 출력하기 보다는 Debug나 Trace를 사용하면 Visual Studio의 출력창을 통해 좀더 깔끔하게 메세지를 표시할 수 있습니다.

 

static void Main(string[] args)
{
    Debug.WriteLine("실행중.");
    Trace.WriteLine("실행중.");
}

 

간혹 프로그램을 빌드하는 이 2가지 모드가 무시되는 경우가 있으나 개발단계에서는 프로그램의 정확한 디버깅을 위해 Debug모드로, 사용자에게 배포할때는 Release모드로 빌드하는 습관을 들이는 것이 좋습니다.

'Programming > .NET' 카테고리의 다른 글

[C#] partial  (0) 2019.02.07
[C#] 비동기 호출(asynchronous call)  (0) 2019.01.23
Debug / Release  (0) 2019.01.15
[ASP.NET MVC] Razor  (0) 2019.01.08
[ASP.NET MVC] URL 다루기  (0) 2018.12.18
[C#] 비트(bit) 연산자  (0) 2018.11.20
0 0
Programming/.NET

1. Model 이용하기

 

public class Employee
{
    public string Name;
    public int Age;
    public string Department;
    public DateTime Doe;
}

▶ Model

 

public ActionResult Index()
{
    Employee em = new Employee() { Name = "홍길동"Department = "기획팀"Age = 33, Doe = new DateTime(2016, 12, 05) };

    return View(em);
}

▶ Controller

 

@model test.Models.Employee

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
<body>
    <div>
        <table style="border:solidborder-width:1px;">
            <thead>
                <tr><th>항목</th><th></th></tr>
            </thead>
            <tbody>
                <tr><td>이름</td><td>@Model.Name</td></tr>
                <tr><td>나이</td><td>@Model.Age</td></tr>
                <tr><td>부서</td><td>@Model.Department</td></tr>
            </tbody>
        </table>
    </div>
</body>
</html>
▶ View

 

2. ViewBag 이용하기

 

public ActionResult Index()
{
    Employee em = new Employee() { Name = "홍길동"Department = "기획팀"Age = 33, Doe = new DateTime(2016, 12, 05) };

    ViewBag.Title = "사원테이블";
    ViewBag.Width = "5px";
    ViewBag.Resignation = false;
    ViewBag.IsMarriage = true;
    ViewBag.IsExecutive = null;

    return View(em);
}

▶ Controller

 

@model test.Models.Employee

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
<body>
    <div>
        퇴사 : <input type="checkbox" checked="@ViewBag.Resignation" /><br />
        결혼 : <input type="checkbox" checked="@ViewBag.IsMarriage" /><br />
        임원 : <input type="checkbox" checked="@ViewBag.IsExecutive" />
        <table style="border:solid; border-width:@ViewBag.Width;">
            <caption>@ViewBag.Title</caption>
            <thead>
                <tr><th>항목</th><th></th></tr>
            </thead>
            <tbody>
                <tr><td>이름</td><td>@Model.Name</td></tr>
                <tr><td>나이</td><td>@Model.Age</td></tr>
                <tr><td>부서</td><td>@Model.Department</td></tr>
            </tbody>
        </table>
    </div>
</body>
</html>
▶ View

 

참고로 checkbox처리의 경우 true만 checked 처리되고 false나 null은 아예 속성처리가 무시됩니다.

 

3. 구문

 

<span>

    @if ((bool)ViewBag.Resignation)
    {
        @: 이직
    }
    else {
        @: 재직
    }
</span>

▶ View

 

Razor 구문은 @문자로 시작합니다. 참고로 문자열은 기본적으로 C#문으로 인식하는데 구문안에서 단순한 문자열을 출력할때는 @: 기호를 사용합니다.

 

public ActionResult Index()
{
    Employee[] em = {
        new Employee() { Name = "홍길동"Department = "기획팀"Age = 33, Doe = new DateTime(2016, 12, 05) },
        new Employee() { Name = "홍길순"Department = "경영팀"Age = 25, Doe = new DateTime(2004, 9, 8) },
        new Employee() { Name = "홍길남"Department = "영업팀"Age = 40, Doe = new DateTime(2012, 4, 17) }
    };

    return View(em);
}

▶ Controller

 

@model test.Models.Employee[]

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
<body>
    <div>
        <table style="border:solid; border-width:@ViewBag.Width;">
            <caption>@ViewBag.Title</caption>
            <thead>
                <tr><th>항목</th><th></th></tr>
            </thead>
            <tbody>
                @foreach (test.Models.Employee em in Model) {
                    <tr><td>이름</td><td>@em.Name</td></tr>
                    <tr><td>나이</td><td>@em.Age</td></tr>
                    <tr><td>부서</td><td>@em.Department</td></tr>
                }
            </tbody>
        </table>
    </div>
</body>
</html>
▶ View

 

배열과 같은 모델의 데이터를 열거할때는 @model 에서 모델형식을 Employee[] 처럼 배열로 지정해야 합니다.

 

4. Namespace

 

@using test.Models
@model test.Models.Employee[]

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
<body>
    <div>
        <table style="border:solid; border-width:@ViewBag.Width;">
            <caption>@ViewBag.Title</caption>
            <thead>
                <tr><th>항목</th><th></th></tr>
            </thead>
            <tbody>
                @foreach (Employee em in Model) {
                    <tr><td>이름</td><td>@em.Name</td></tr>
                    <tr><td>나이</td><td>@em.Age</td></tr>
                    <tr><td>부서</td><td>@em.Department</td></tr>
                }
            </tbody>
        </table>
    </div>
</body>
</html>
▶View

 

Namespace도 일반 C#구문과 마찬가지로 using을 사용합니다. 다만 Razor작성 규칙에 따라 @를 붙여야 합니다.

'Programming > .NET' 카테고리의 다른 글

[C#] 비동기 호출(asynchronous call)  (0) 2019.01.23
Debug / Release  (0) 2019.01.15
[ASP.NET MVC] Razor  (0) 2019.01.08
[ASP.NET MVC] URL 다루기  (0) 2018.12.18
[C#] 비트(bit) 연산자  (0) 2018.11.20
[ASP.NET MVC] 액션 메서드(Action Method)  (0) 2018.11.13
0 0
1
블로그 이미지

클리엘