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
'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 |