Programming/C#

C# 크로스 스레드(Cross Thread)란? (에러 발생 이유 / 해결 방법)

JeongKyun 2021. 12. 29.

서론

Winform에서 스레드 환경을 구성해서 폼을 제어하다보면 아래의 사진과 같은 크로스 스레드 에러가 빈번하게 발생하는것을 확인 할 수 있을 것이다.

 

 

에러 내용 : 크로스 스레드 작업이 잘못되었습니다. 'Textbox' 컨트롤이 자신이 만들어진 스레드가 아닌 스레드에서 액세스 되었습니다.

 

 

에러가 발생하는 이유 ?

폼 구동 시 실행되는 하나의 쓰레드에서 Winform 컨트롤을 관리하게 되는데 해당 특정 스레드가 관리하고 있는 컨트롤을 다른 스레드가 액세스하였을 때 발생한다.

다시 말하면 winform 컨트롤을 생성한 스레드가 아닌 다른 스레드가 속성을 바꾸는 작업을 요청했을 때 크로스 스레드 예외가 발생하는 것이다.

 

근데, 크로스 스레드는 특이한게 Debug모드에서만 실행되고 Release모드에서는 실행이 안된다고 한다.

그 이유를 찾아봤는데 Release모드는 옵션값이 Default로 checkForIllegalCrossThreadCalls = false;가 되어있어서 그렇고 해당 값을 true로 바꾸면 Release모드에서도 오류를 발생시킬 수 잇다고 한다.

 

해결 방법은 ?

이 에러를 해결하는 방법들을 정리해보자.

 

CheckForIllegalCrossThreadCalls (비추천)
CheckForIllegalCrossThreadCalls = false;


위의 방법을 사용하면 대게 해결은 다 된다. 그렇지만 해당 방법은 크로스 쓰레드에 대한 위반 검사를 하지않겠다는 선언이며, 이거를 마구 사용 해 놓으면 나중에 갑자기 뻗는 이유중 하나가 될 수 있는 것이다. 그래서 최대한 지양하는것이 좋다. 그래서 위 방법보단 아래의 방법을 사용하는것을 추천한다.

 

 

 

InvokeRequired (추천)

Windows Form의 모든 컨트롤에는 InvokeRequired 속성을 제공하는데,

이 속성 값이 True라면 현재 스레드가 자신을 생성한 스레드가 아닌 것이고,

False이면 현재 스레드가 자신을 생성한 스레드 인 것이다.

 

그래서 이 속성값을 활용하여 크로스 스레드의 발생 원인의 값을 구분 지을 수 있는것이다.

구분 지은 후 Invoke 메소드를 활용하여 크로스 스레드를 방지할 수 있다.

아래의 예시를 보자.

 

예시 1) 인자가 있는 메소드를 호출 하는 경우 (Callback 메소드 사용)

        delegate void fnSetTextBoxCallback(string contents);
        
        private void SetTextBox(string contents)
        {
            //생성된 스레드가 아닌 다른 스레드에서 호출될 경우 true
            if (this.textBox.InvokeRequired)
            {
                this.Invoke(new fnSetTextBoxCallback(SetTextboxInput), new object[] 
                {
                	contents 
                });
            }
            else
            {
                this.textBox.Text = contents;
            }
        }
        private void SetTextboxInput(string contents)
        {
            this.textBox.Text = contents;
        }

 

예시 1의 방식은 콜백 메소드를 대리자로 구현하여 내용을 넣는 방식이다. 근데 인자가 없는 메소드를 호출할 때는 아래의 방식대로 구현해주면 된다.

 

 

예시 2) 인자가 없는 메소드를 호출 하는 경우 (MethodInvoker 사용) 

public static void SetTextBox(TextBox tb, string contents)
{
    //생성된 스레드가 아닌 다른 스레드에서 호출될 경우 true
    if (tb.InvokeRequired)
    {
        tb.Invoke(new MethodInvoker(delegate()
        {
            tb.Text = contents;
        }));
    }
    else
    {
        tb.Text = contents;
    }
}


/*
 함수로 안만들고 그냥 크로스 스레드 생기는 컨트롤만 묶어주고싶다하면 InvokeRequired 안쓰고
 아래처럼 그냥 controll.invoke로 묶어줘서 사용해주면된다.
*/

        tb.Invoke(new MethodInvoker(delegate()
        {
            tb.Text = "HI Jeongkyun";
        }));

예시 2의 방식은 스레드를 이용해 textbox에 내용을 넣어야 하는 상황이 생긴다면 위의 방식처럼 함수처럼 매개변수만 맞춰서 사용하면 된다.

 

이렇게 위의 방식들로 진행하면은 크로스 스레드 문제로 인해 스트레스 받는일은 없을 것이다.

크로스 스레드로 이제 그만 스트레스 받자!

 

반응형

댓글

💲 많이 본 글