Windows Forms에서 UI 스레드인지 여부를 결정합니다. Control.Invoke는 무엇을하고 InvokeRequired는 무엇을 어떻게 조사하고 있습니까?

6672 단어 .NETFrameworkWin32API

소개



Windows Forms 응용 프로그램 등에서 멀티스레드 응용 프로그램을 만들 때는 폼이나 컨트롤에 작성한 스레드에서 액세스해야 합니다.

제목 애플리케이션



이러한 느낌의 폼으로, 1초마다 현재 시각을 갱신 표시하는 어플리케이션을 작성해 보겠습니다.



폼을 작성한 thread로부터 태스크를 생성(=다른 thread)해 갱신 표시를 시키는 구조의, 이런 느낌의 코드를 실행시키면(자), 태스크내에서 라벨 컨트롤 (label1)의 텍스트를 갱신하려고 했을 때 「 InvalidOperationException : 유효하지 않은 스레드 간의 조작 : 컨트롤이 작성된 스레드 이외의 스레드가 컨트롤 'label1'에 액세스했습니다."예외가 발생합니다.



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

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

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            Task.Run(() => 
            {
                for(; ; )
                {
                    label1.Text = DateTime.Now.ToString("HH:mm:ss"); // <- 例外発生
                    System.Threading.Thread.Sleep(1000);
                }
            });
        }
    }
}


독점적으로 작업용으로 만든 스레드(작업자 스레드)에서 폼이나 컨트롤 등이 속하는 스레드(UI 스레드)로 처리를 하려면 Control.Invoke 메서드를 사용해야 하며 레이블 컨트롤의 Text 속성에 쓰는 처리 의 코드를 다음과 같이 변경하면 잘 동작합니다.
            Task.Run(() =>
            {
                for (; ; )
                {
                    Invoke((MethodInvoker)delegate
                    {
                        label1.Text = DateTime.Now.ToString("HH:mm:ss");
                    });
                    System.Threading.Thread.Sleep(1000);
                }
            });

과연, 이 Invoke 메소드는, 무엇을 하고 있는 것일까요?

Invoke 메서드 처리



.NET Framework (4.7.2) 소스 코드를 읽어 보았습니다. 꽤 대략적으로 쓰면 다음을하고 있습니다.
  • Win32API 의 GetWindowsThreadProcessId 함수를 사용해, 액세스 하고자 하는 thread와, 대상의 컨트롤 (핸들이 작성되어 있지 않으면 그 부모 컨트롤)이 속하는 thread가 일치하는지 조사한다.
  • 처리하는 메소드 정보를 모으는 큐에 메소드 정보 (메소드, 인수, 실행 문맥, 반환값이나 발생한 예외의 수신 장소, System.IAsyncResult 를 구현)를 추가해, 1.로 thread가 다른 경우 윈도우 메세지 (스레드 콜백용 : 4.7.2에서는 "WindowsForms12_ThreadCallbackMessage")를 POST한다. 1. 에서 같은 스레드라면, 스스로 큐의 내용을 꺼내 메소드를 호출한다 (큐가 비워질 때까지 반복한다).
  • 큐에 추가한 대상의 메소드가 종료할 때까지 대기( IAsyncResult.AsyncWaitHandle ) 해, 그 반환값을 돌려준다.

  • POST 된 대상 UI 스레드의 메시지 펌프 (WndProc)에서 스레드 콜백에 ​​대한 창 메시지를 받으면 큐를 검색하고 메서드를 호출합니다 (큐가 비어있을 때까지 반복).

    덧붙여서 Control.InvokeRequired 프로퍼티은, 상기 중 1. 의 처리를 실시해, 같은 thread이면 false, 다른 경우는 true 를 돌려줍니다.

    결론



    조금 흥미가 있었으므로, 조사해 보았습니다. .NET Framework 소스 코드를 쉽게 살펴볼 수 있는 Visual Studio 확장 프로그램인 Ref12은 항상 신세를 지고 있습니다.

    좋은 웹페이지 즐겨찾기