[c#] 호출 (대리자)

누구든지이 링크에 작성된이 진술을 설명해 주시겠습니까?

Invoke(Delegate):

컨트롤의 내부 창 핸들 을 소유하는 스레드에서 지정된 대리자를 실행합니다 .

아무도 이것이 무엇을 의미하는지 설명 할 수 있습니까? (특히 굵은 글씨) 나는 그것을 명확하게 이해할 수 없습니다



답변

이 질문에 대한 답은 C # 컨트롤의 작동 방식에 있습니다.

Windows Forms의 컨트롤은 특정 스레드에 바인딩되며 스레드로부터 안전하지 않습니다. 따라서 다른 스레드에서 컨트롤의 메서드를 호출하는 경우 컨트롤의 호출 메서드 중 하나를 사용하여 적절한 스레드에 대한 호출을 마샬링해야합니다. 이 속성은 호출 메서드를 호출해야하는지 여부를 결정하는 데 사용할 수 있으며, 컨트롤을 소유하는 스레드를 모르는 경우 유용 할 수 있습니다.

에서 Control.InvokeRequired

실제로 Invoke는 호출하는 코드가 컨트롤이 “존재하는”스레드에서 발생하여 교차 스레드 예외를 효과적으로 방지하도록 보장합니다.

역사적 관점에서 .Net 1.1에서는 실제로 허용되었습니다. 이것이 의미하는 바는 모든 백그라운드 스레드에서 “GUI”스레드에서 코드를 실행하고 시도 할 수 있으며 대부분 작동합니다. 때로는 다른 작업을 수행하는 동안 GUI 스레드를 효과적으로 중단했기 때문에 앱이 종료되는 경우가 있습니다. 이것은 Cross Threaded Exception 입니다. GUI가 다른 것을 그리는 동안 TextBox를 업데이트하려고한다고 상상해보십시오.

  • 어떤 조치가 우선합니까?
  • 둘 다 한꺼번에 일어날 수도 있습니까?
  • GUI가 실행해야하는 다른 모든 명령은 어떻게됩니까?

사실상, 예상치 못한 결과가 많이 발생할 수있는 대기열을 중단하고 있습니다. Invoke는 실제로 원하는 작업을 해당 대기열로 가져 오는 “정중 한”방법이며이 규칙은 throw 된 InvalidOperationException을 통해 .Net 2.0부터 적용되었습니다 .

실제로 뒤에서 일어나는 일과 “GUI 스레드”가 무엇을 의미하는지 이해하려면 메시지 펌프 또는 메시지 루프가 무엇인지 이해하는 것이 유용합니다.

이것은 실제로 ” What is a Message Pump ” 질문에 이미 답변되어 있으며 컨트롤과 상호 작용할 때 사용하는 실제 메커니즘을 이해하기 위해 읽는 것이 좋습니다.

유용한 기타 자료는 다음과 같습니다.

Begin Invoke의 문제

Windows GUI 프로그래밍의 기본 규칙 중 하나는 컨트롤을 만든 스레드 만 해당 내용에 액세스 및 / 또는 수정할 수 있다는 것입니다 (몇 가지 문서화 된 예외 제외). 다른 스레드에서 시도하면 교착 상태에서 예외, 절반 업데이트 된 UI에 이르기까지 예측할 수없는 동작이 발생합니다. 다른 스레드에서 컨트롤을 업데이트하는 올바른 방법은 적절한 메시지를 응용 프로그램 메시지 큐에 게시하는 것입니다. 메시지 펌프가 해당 메시지를 실행하게되면 해당 메시지를 생성 한 동일한 스레드에서 컨트롤이 업데이트됩니다 (메시지 펌프는 기본 스레드에서 실행 됨).

그리고 대표적인 샘플을 사용하여 코드가 많은 개요를 보려면 다음을 수행하십시오.

잘못된 크로스 스레드 작업

// the canonical form (C# consumer)

public delegate void ControlStringConsumer(Control control, string text);  // defines a delegate type

public void SetText(Control control, string text) {
    if (control.InvokeRequired) {
        control.Invoke(new ControlStringConsumer(SetText), new object[]{control, text});  // invoking itself
    } else {
        control.Text=text;      // the "functional part", executing only on the main thread
    }
}

InvokeRequired에 대한 감사를 받으면 이러한 호출을 래핑하기위한 확장 메서드 사용을 고려할 수 있습니다. 이것은 Stack Overflow 질문 Cleaning Up Code Littered with Invoke Required 에서 다룹니다 .

또한 흥미로울 수있는 역사적으로 일어난 일에 대한 추가 기록 도 있습니다.


답변

Windows Forms의 컨트롤 또는 창 개체는 핸들 (HWND라고도 함)로 식별되는 Win32 창을 둘러싼 래퍼 입니다. 컨트롤로 수행하는 대부분의 작업은 결국이 핸들을 사용하는 Win32 API 호출이 발생합니다. 핸들은 생성 한 스레드 (일반적으로 주 스레드)가 소유하며 다른 스레드에서 조작해서는 안됩니다. 어떤 이유로 다른 스레드의 컨트롤로 작업을 수행해야하는 경우을 사용 Invoke하여 주 스레드에 사용자를 대신하여 수행하도록 요청할 수 있습니다 .

예를 들어 작업자 스레드에서 레이블의 텍스트를 변경하려면 다음과 같이 할 수 있습니다.

theLabel.Invoke(new Action(() => theLabel.Text = "hello world from worker thread!"));


답변

컨트롤을 수정하려면 컨트롤이 생성 된 스레드에서 수행해야합니다. 이 Invoke메서드를 사용하면 연결된 스레드 (컨트롤의 기본 창 핸들을 소유하는 스레드)에서 메서드를 실행할 수 있습니다.

아래 샘플에서 thread1은 SetText1이 다른 스레드에서 textBox1.Text를 수정하려고하기 때문에 예외를 발생시킵니다. 그러나 thread2에서는 SetText2의 Action이 TextBox가 생성 된 스레드에서 실행됩니다.

private void btn_Click(object sender, EvenetArgs e)
{
    var thread1 = new Thread(SetText1);
    var thread2 = new Thread(SetText2);
    thread1.Start();
    thread2.Start();
}

private void SetText1()
{
    textBox1.Text = "Test";
}

private void SetText2()
{
    textBox1.Invoke(new Action(() => textBox1.Text = "Test"));
}


답변

Invoke((MethodInvoker)delegate{ textBox1.Text = "Test"; });


답변

실제적으로는 대리자가 주 스레드에서 호출된다는 것을 의미합니다. 이는 Windows 컨트롤의 경우 주 스레드에서 속성을 업데이트하지 않으면 변경 사항이 표시되지 않거나 컨트롤에서 예외가 발생하기 때문에 중요합니다.

패턴은 다음과 같습니다.

void OnEvent(object sender, EventArgs e)
{
   if (this.InvokeRequired)
   {
       this.Invoke(() => this.OnEvent(sender, e);
       return;
   }

   // do stuff (now you know you are on the main thread)
}


답변

this.Invoke(delegate)this.Invoke()주 스레드 / 생성 된 스레드 에서 대리자를 인수로 호출하고 있는지 확인하십시오 .

Thumb 규칙은 기본 스레드를 제외하고는 양식 컨트롤에 액세스하지 않는다고 말할 수 있습니다.

Invoke () 사용에 대해 다음 줄이 의미가있을 수 있습니다.

    private void SetText(string text)
    {
        // InvokeRequired required compares the thread ID of the
        // calling thread to the thread ID of the creating thread.
        // If these threads are different, it returns true.
        if (this.textBox1.InvokeRequired)
        {
            SetTextCallback d = new SetTextCallback(SetText);
            this.Invoke(d, new object[] { text });
        }
        else
        {
            this.textBox1.Text = text;
        }
    }

스레드 풀 스레드 (예 : 작업자 스레드)를 만들더라도 주 스레드에서 실행되는 상황이 있습니다. 새 스레드를 만들지 않습니다. 추가 명령을 처리하기 위해 주 스레드를 사용할 수 있습니다. 따라서 먼저 현재 실행중인 스레드가 this.InvokeRequiredtrue를 반환 하는 경우 현재 코드가 작업자 스레드에서 실행되고 있으므로 this.Invoke (d, new object [] {text});

그렇지 않으면 UI 컨트롤을 직접 업데이트합니다 (여기에서 메인 스레드에서 코드를 실행하고 있음을 보장합니다.)


답변

즉, 백그라운드 작업자 또는 스레드 풀 스레드에서 해당 메서드를 호출하더라도 대리자가 UI 스레드에서 실행됩니다. UI 요소에는 스레드 선호도 가 있습니다 . UI 스레드라는 하나의 스레드와 직접 대화하는 것을 좋아합니다. UI 스레드는 컨트롤 인스턴스를 만든 스레드로 정의 되므로 창 핸들과 연결됩니다. 그러나 그 모든 것이 구현 세부 사항입니다.

요점은 UI 스레드가 아닌 다른 스레드에서 수행 할 수 없기 때문에 UI에 액세스 할 수 있도록 작업자 스레드에서이 메서드를 호출한다는 것입니다 (라벨의 값 변경 등) .