Winform에서 invoke없이 UI요소에 접근이 가능한가요?

“Winform에서 일반적으로 워커스레드에서 UI컨트롤에 접근할 때는 UI Thread역할을 수행하는 Main Thread에서 Invoke를 통해 그 부분만 SynchoronizationContext에 작업을 위임하여 처리한다.”

저는 이렇게 알고 있는데요.

VS를 통해 디버깅모드로 수행하면 InvalidOperationException을 통해 크로스스레드 오류가 발생합니다. 이 부분은 예상과 같은 부분인데, 이걸 VS를 통해서 실행하지 않고 직접 빌드된 파일을 실행하면 오류가 발생되지도 않고 정상 작동합니다.

코드는 아래와 같습니다.

using System;
using System.Threading.Tasks;
using System.Windows.Forms;

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

        private async void button1_Click(object sender, EventArgs e)
        {
            await Task.Run(async () =>
            {
                for (int i = 0; i < 10; i++)
                {
                    textBox1.Text = i.ToString();
                    textBox2.Text = i.ToString();
                    textBox3.Text = i.ToString();
                    await Task.Delay(500);
                }
            });
        }
    }
}

.NET 5에서 텍스트 박스 3개랑 버튼 1개 올려놓고 테스트한 것인데요. 아무런 설정도 건들지 않고 이렇게만 했습니다.
이렇게 해서 정상작동하는 작업을 신뢰할 수 있을까요?

1개의 좋아요

일단 Windows Forms는 UI를 처리하는 스레드가 아닌 다른 스레드가 UI 요소를 변경할 수 없도록 보호하는 것이 기본 정책입니다. 그래서 코드가 정상적으로 작동하는 것처럼 보이더라도, async 키워드를 붙여 UI 스레드가 아닌 다른 스레드가 처리하도록 분리했다면 Invoke 함수를 이용해서 대행하는 것이 맞습니다.

만약 UI를 고쳐야 하는 빈도가 높다면, 구현 방법을 조금 다르게 만드는 것이 좋습니다. 표시해야 할 값 자체를 별도의 데이터로 관리하고, UI에서 이것을 읽도록 처리한다던가 하는 형태가 좋습니다.

참고로 이건 BackgroundWorker 같은 컴포넌트를 사용할 때에도 마찬가지입니다. DoWork 안에서는 비동기로 처리하고 싶은 순수 로직만 담고, RunWorkerCompleted 이벤트로 결과값만 pass해서 UI를 고치도록 하는 패턴도 많이 쓰입니다.

네 저도 Invoke 원리를 간략하게 나마 알고 있기에 이제는 외워서 당연하게 쓰고있었습니다.

보통 개발할때 여러 오류를 해결한뒤에 배포하니까 최종 배포했던 프로그램에서는 당연히 Invoke처리가 되어서 하고 있었습니다.

그런데 지인분께 저 코드가 VS를 통하지 않고 바로 실행하면 문제가 없이 돌더라고 들어서 소스코드를 그대로 받아서 실행해보니 정말 오류 없이 정상 동작했습니다. debug로 빌드한 어셈블리나, release로 빌드한 어셈블리는 차이가 없이 오류 없는 정상 동작을 했습니다.

그래서 그 원리가 궁금했습니다. 혹 내부적으로 빌드한 결과물 자체는 invoke가 처리되는 것인지…그럴리는 없다고 생각은 하지만요…ㅎㅎ

Windows Forms에서 Invoke 없이 UI 요소에 접근이 가능합니다. 하지만 Windows Forms ControlsThread Safe 하지 않기 때문에 오동작 할 여지가 있으므로 디버그로 띄울 때 크로스 스레드 InvalidOperation예외를 띄우는게 기본 설정입니다.

.NET Core (.NET 5)에서 혹시 컨트롤들이 Thread Safe해졌나 해서 살펴봤더니 여전히 동일한 정책이더군요. 즉,

잘 돌아가는 것 처럼 보일 뿐이라고 생각합니다.

다음의 글도 참고해 보세요. ^^

UI 요소의 접근은 반드시 그 UI를 만든 스레드에서! - 두 번째 이야기
; .NET Framework: 1022. UI 요소의 접근은 반드시 그 UI를 만든 스레드에서! - 두 번째 이야기

3개의 좋아요

헉…내용 정독했습니다!! 정말 감사드립니다. 시원하게 해결되었습니다!!

1개의 좋아요