본문 바로가기

Study_C#

[C#] Invoke, InvokeRequired, Delegate

728x90
반응형

Invoke / InvokeRequired 사용 이유

  • C#의 경우 보통 Main Thread만이 UI에 대한 Control을 조작할 수 있게 함 (Thread Affinitiy)
    • 응용 프로그램이 실행될 경우 기본적으로 하나의 Thread가 발생 : Main Thread
    • 이 Thread는 Main Form의 Event 처리 및 Main Form의 각종 Control들에 대한 읽기 / 쓰기 작업 수행
    • Main Form에서 다른 Form을 띄울 경우에도 기본적으로 Main Thread가 자식 Form의 Control까지 모두 소유

 

  • 이 때, Main Thread 외의 Thread에서 UI 상의 Control(Label, Textbox 등)에 접근하려 할 경우
    아래의 Cross Thread Error 발생
    • 별도의 Thread에서 Form의 Control에 접근하면 Data가 깨질 수 있음

 

  • 이 경우, Invoke method를 통해 Control에 접근할 수 있다
  • Invoke : 다른 Thread에서 직접 접근할 수 없는 Control에 대한 작업을 Main Thread에 위임하는 method
    • Delegate(대리자)를 통해 Main Thread에 Control 접근 작엄을 위임하여 수행하게 함
    • 실행하고자 하는 Method의 Delegate를 실행시킴
  • 별도의 Thread에서 main_form.Invoke(method1) 을 호출하는 것은
    별도의 Thread가 main_form을 소유한 Thread에게 method1 의 호출을 위임한다는 뜻

 

- 예제 코드 : 반복 덧셈 계산 프로그램

using System;
using System.Windows.Forms;

namespace Study_WinForm
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void btn_Calculate_Click(object sender, EventArgs e)
        {
            int count = 0;

            if(int.TryParse(tbx_Count.Text, out count))
            {
                var result = Calculate(count);
                lbl_Result.Text = result.ToString();
            }
            else
            {
                lbl_Result.Text = "유효한 숫자를 입력해주세요";
            }
        }

        public int Calculate(int cnt)
        {
            int result = 0;

            for (int i = 0; i < cnt; i++)
            {
                result += 1;
            }
            return result;
        }

        private void btn_Clear_Click(object sender, EventArgs e)
        {
            lbl_Result.Text = "0";
            tbx_Count.Text = "0";
        }

    }
}

 

 

 

  • 입력받은 횟수만큼 1을 반복적으로 더하는 Form
  • 위 Form에 1000000000 정도의 수를 입력할 경우 UI가 잠시 멈추는 현상이 발생함

(버벅이는 동영상 추가)

  • 이는 긴 시간의 반복적인 계산 수행 동안 Main Thread가 UI를 관리하지 못하게 되는 것 이 원인

 

  • 위 현상을 해결하기 위해 별도의 Thread에서 계산을 수행하게 함
using System;
using System.Windows.Forms;
using System.Threading;

namespace Study_WinForm
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void btn_Calculate_Click(object sender, EventArgs e)
        {
            int count = 0;

            if(int.TryParse(tbx_Count.Text, out count))
            {
                Thread calthread = new Thread(() =>
                {
                    var result = Calculate(count);
                    lbl_Result.Text = result.ToString();
                });
                calthread.Start();
            }
            else
            {
                lbl_Result.Text = "유효한 숫자를 입력해주세요";
            }
        }

        public int Calculate(int cnt)
        {
            int result = 0;

            for (int i = 0; i < cnt; i++)
            {
                result += 1;
            }
            return result;
        }

        private void btn_Clear_Click(object sender, EventArgs e)
        {
            lbl_Result.Text = "0";
            tbx_Count.Text = "0";
        }

    }
}

 

 

Thread calthread = new Thread(() =>
                {
                    var result = Calculate(count);
                    lbl_Result.Text = result.ToString();
                });
                calthread.Start();

 

 

  • 추가된 Statement
  • Thread calthread = new Thread(() => : Thread Class의 새로운 Object 'calthread' 생성
    • Thread : .NET Framework에서 제공하는, 코드의 병렬 실행을 지원하는 Class 
  • { ... } 부분 : 새 Thread에서 실행할 코드 부분
    • Calcutate method 호출 후 return 값을 String으로 변환하여 result에 대입
    • 이후 lbl_Result의 text에 해당 값을 대입
  • thread.Start() : Thread 실행
    • UI Thread와 무관하게 동작하는 Thread

 

    • 단 위와 같이 별도의 Thread에서 UI의 Control에 접근을 시도할 경우 아래와 같은 Cross Thread 관련 Error 발생
      • Main Thread에서 생성한 Control을 'thread' Thread에서 접근하려 해 발생하는 Error

 

  • 이를 해결하기 위해 'Invoke' method를 통해 Main Thread에게 UI에 대한 작업을 위임
using System;
using System.Windows.Forms;
using System.Threading;

namespace Study_WinForm
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void btn_Calculate_Click(object sender, EventArgs e)
        {
            int count = 0;

            if(int.TryParse(tbx_Count.Text, out count))
            {
                Thread calthread = new Thread(() =>     
                {
                    var result = Calculate(count);
                    if (this.InvokeRequired)
                    {
                        this.Invoke((MethodInvoker)(() =>
                        {
                            lbl_Result.Text = result.ToString();
                        }));
                    }
                    else
                    {
                        lbl_Result.Text = result.ToString();
                    }
                    
                });
                calthread.Start();
            }
            else
            {
                lbl_Result.Text = "유효한 숫자를 입력해주세요";
            }
        }

        public int Calculate(int cnt)
        {
            int result = 0;

            for (int i = 0; i < cnt; i++)
            {
                result += 1;
            }
            return result;
        }

        private void btn_Clear_Click(object sender, EventArgs e)
        {
            lbl_Result.Text = "0";
            tbx_Count.Text = "0";
        }

    }
}

 

 

if (this.InvokeRequired)
    {
        this.Invoke((MethodInvoker)(() =>
        {
            lbl_Result.Text = result.ToString();
        }));
    }
    
else
{
    lbl_Result.Text = result.ToString();
}

 

  • Control에 접근하고자 하는 Thread에서 InvokeRequired member 값을 가져옴
    • True Return 시 Invoke method 호출이 필요한 상태
    • False Return 시 Control에 직접 접근해도 문제가 없는 상태
  • Invoke가 필요할 경우 새 delegate method를 생성하여 해당 Control을 제어

 

  • 다른 Thread로부터 호출되어 Invoke가 필요한 상태를 확인해서 true / false Return
  • Invoke가 필요할 경우 invoke method에 의해 호출될 Delegate로 넘겨서 실행시키면 안정성있게 UI 갱신

 

- Delegate

  • Method를 매개 인자로 사용할 수 있는 기능
    • Method 자체를 인자로 넘겨주는 '형식'
  • 한번에 여러 Method 호출 가능

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


참고 자료 : 

https://bufferover.tistory.com/3

 

C# Invoke를 사용해 크로스 스레드 문제를 해결하는 방법

문제 원인 동시성이 있는 멀티 스레드 프로그램 환경에서 특정 스레드에서 생성된 Win Form 컨트롤 ( TextBox, ListView, Label, … )을 다른 스레드에서 접근할 때 발생한다. 스레드에서 안전한 방식으로

bufferover.tistory.com

 

https://cartiertk.tistory.com/67

 

Invoke 함수란 무엇인가?

Invoke 함수란 무엇인가? (법, 규칙등을)들먹이다. (누구의 이름을)부르다, (프로그램등을)불러오다. 하나의 Form을 다른 thread에서 접근하게 될 경우에 기존의 Form과 충돌이 날 수 있다. 이 때 invoke

cartiertk.tistory.com

 

https://carmack-kim.tistory.com/24

 

[Invoke & BeginInvoke] 1. 다른 Thread 에서 UI 접근하기 (1)

C# 멀티쓰레드와 Invoke에 관해 정리를 잘해놓으신분이 있어서 C# Multi Thread와 Invoke에 관해 포스팅한다. 출처 : http://ddochea.tistory.com/11?category=568955 [또치의 삽질 보관함] 오랜 시간이 걸리는 작업에

carmack-kim.tistory.com

 

https://velog.io/@ash028/C-%EB%8D%B8%EB%A6%AC%EA%B2%8C%EC%9D%B4%ED%8A%B8-delegate

 

[C#] Delegate (델리게이트)

메서드 자체를 인자로 넘겨주는 ‘형식’이다.즉 델리게이트를 통해 메서드를 매개 변수로 전달할 수 있다.함수가 아니라 '형식'이라는 것에 유의하자.우리가 게임을 하는데 버튼을 누르면 플

velog.io

 

https://www.csharpstudy.com/CSharp/CSharp-delegate.aspx

 

C# delegate 1 - C# 프로그래밍 배우기 (Learn C# Programming)

C# delegate의 개념 C# delegate는 C/C++의 함수 포인터와 비슷한 개념으로 메서드 파라미터와 리턴 타입에 대한 정의를 한 후, 동일한 파라미터와 리턴 타입을 가진 메서드를 서로 호환해서 불러 쓸 수

www.csharpstudy.com

 

https://woojoolog.tistory.com/13

 

C# delegate 개념과 사용 이유

1. delegate 개요 처음 C# delegate를 접했을 때 왜 사용해야 하는지 이해하기가 힘들었습니다. 그래서 왜 사용해야 하는지에 대해 자세히 설명하겠습니다. C#에는 대리자(delegate)라는 개념이 존재합니

woojoolog.tistory.com

 

 

 

 

728x90
반응형

'Study_C#' 카테고리의 다른 글

[C#] Various Keywords, Methods  (0) 2024.07.30
[C#] Get, Set 키워드  (0) 2024.07.30
[C#] Using의 2가지 사용법  (0) 2024.07.30
[C#] 비동기 관련  (0) 2024.07.06
[C#] 자료형 정리  (0) 2024.07.06