크로스 스레드
< 크로스 스레드란 ? >
크로스 스레드 문제는 멀티 스레드 환경에서 발생한다. Form 컨트롤러를 생성한 UI 스레드 외에 다른 스레드가 컨트롤러에 직접 접근할 경우 발생하는 에러다. 이를 통해 개발자에게 스레드 문제가 있음을 알려준다.
이는 디버그 모드에서만 발생하며, Thread Safety를 보장하지 않는다. 크로스 스레드가 발생하면, Program이 실행되다가, OS 상황이 안좋아지면, 프로그램에 영향을 줄 수 있다.
< Thread Safety란? >
Thread Safety는 변수, 객체 또는 다른 공유 자원이 여러 스레드로부터 동시에 접근이 이루어져도 프로그램 실행에 문제가 없음을 의미한다. 즉, 공유 자원의 상태가 예상 가능하고 일관된 상태를 유지해야 한다.
< UI-Thread 외 다른 스레드에서 Controller를 안전하게 하는 방법 >
크로스 스레드 문제를 해결하기 위해 Invoke와 Delegate 조합을 사용하여 동기적으로 UI 스레드에게 해당 함수를 호출하도록 요청할 수 있다. 이렇게 하면 UI 스레드가 컨트롤러를 안전하게 업데이트한다.(비동기로 할려면 BeginInvoke를 사용해도 된다.)
또 매게 변수가 없고 Return Type이 없는 것은 new Action을 통해서도 할 수 있다.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
Thread thread2 = null;
private void Form1_Load(object sender, EventArgs e)
{
try
{
thread2 = new Thread(new ThreadStart(SendLogic));
thread2.IsBackground = true;
thread2.Priority = ThreadPriority.Normal;
thread2.Start();
}
catch (Exception ex)
{
//에러 로그기록
}
}
private void SendLogic()
{
try
{
this.Invoke(new Action(() =>
{
label1.Text = "bb";
label2.Text = "bb";
}));
//MethodInvoker를 사용하는 이유 : Invoke 메서드와 MethodInvoker를 사용하면 UI 스레드에서 코드를 안전하게 실행할 수 있기 때문
Invoke((MethodInvoker)delegate {
label1.Text = "cc";
label2.Text = "cc";
});
}
catch (Exception ex)
{
//에러 로그기록
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
try
{
if (thread2.IsAlive)
{
thread2.Abort();
}
thread2 = null;
}
catch (Exception ex)
{
//에러 로그기록
}
}
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e) :
이 부분은 Form1의 FormClosing 이벤트 핸들러다. 이 핸들러는 폼이 닫히기 직전에 호출된다. 여기서 sender는 이벤트를 발생시킨 객체를 나타내고, e는 이벤트와 관련된 추가 정보를 담고 있다.
try : 코드 블록 내에서 발생할 수 있는 예외를 처리하기 위해 try 블록 사용.
if (thread2.IsAlive) : thread2가 여전히 실행 중인지 확인. IsAlive 속성은 스레드가 아직 실행 중인 경우 true를 반환한다.
thread2.Abort() : thread2가 실행 중인 경우, Abort() 메서드를 호출하여 스레드를 중단시킨다. ※(이 메서드는 스레드를 즉시 중지하는 것이 아니라, 스레드가 중단될 수 있을 때 중단하도록 요청한다.)
thread2 = null; : thread2 참조를 null로 설정하여 더 이상 참조되지 않도록 한다. 이렇게 하면 가비지 컬렉터가 나중에 메모리를 정리할 수 있다.
catch (Exception ex) : try 블록 내의 코드가 예외를 발생시킬 경우, 이 catch 블록이 실행된다.
//에러 로그기록 : 일반적으로 이 부분에서 예외 정보를 로그 파일이나 콘솔에 기록하는 코드를 작성합니다. 이를 통해 나중에 문제를 분석하고 해결할 수 있다.
Reference : C#.NET 0.5년차~3년차(파트1)
※ 블로그에 있는 모든 강의와 책의 내용은 저작권자(출판사/저자,공동저자)에게 직접 허락을 받아 작성되었습니다.