3. Implementation/C#

비동기로 실행하기

SSKK 2010. 12. 8. 03:03
UI 업데이트를 비동기로 실행하는 코드 예이다. C# 에서는 PostMessage 와 유사한 기능을 아래처럼 쉽게 구현할 수 있습니다.


public class FormFoo : Form
{
   Label label = new Label();
   public static void Main()
   {
       Application.Run(new FormFoo());
   }

   public FormFoo()
   {
      InitializeComponent();

      Thread t = new Thread(new ThreadStart(ChangeLabel));
      t.Start();
   }

   private void ChangeLabel()
   {
       for (int i = 0; i<100; ++i)
       {
          SetLabelText(i);
          Thread.Sleep(1000);
       }
   }
   private delegate void SetLabelTextDelegate(int number);
   private void SetLabelText(int number)
   {
      // label.Text = number.ToString();
      // Do NOT do this, as we are on a different thread.

      // Check if we need to call BeginInvoke.
      if (this.InvokeRequired)
      {
         // Pass the same function to BeginInvoke,
         // but the call would come on the correct
         // thread and InvokeRequired will be false.
         this.BeginInvoke(new SetLabelTextDelegate(SetLabelText), 
                                          new object[] {number});

         return;
      }

      label.Text = number.ToString();
   }
}

참고한 사이트의 중요한 부분은 다음과 같습니다.

Why and when to call BeginInvoke

With all that stuff inside our head now, we can easily figure out the reason for the existence of BeginInvoke. It essentially does a PostMessage. Whenever you want to update a control from a thread that didn't create it, instead of directly calling the method/property to update it, you need to wrap it in a BeginInvoke call. According to MSDN: "There are four methods on a control that are safe to call from any thread: Invoke,BeginInvokeEndInvoke, and CreateGraphics. For all other method calls, you should use one of the invoke methods to marshal the call to the control's thread". One of the invoke methods, you say, what are the others? Well, the Control class provides one property and two methods to do the stuff we discussed.

  • InvokeRequired: This bool property returns true if the thread on which this property is called is not the thread that created this control. Basically, if InvokeRequired returns true, you need to call one of the two Invoke methods.
  • BeginInvoke: This is a functionally similar to the PostMessage API function. It posts a message to the queue and returns immediately without waiting for the message to be processed. BeginInvoke returns an IAsyncResult, just like the BeginInvoke method on any delegate. And you can use IAsyncResult to wait for the message to be processed, just as usual. And you can call EndInvoke to get return values or outparameter values, as usual.
  • Invoke: This is like the SendMessage API function in that it waits till the message gets processed, but it does not do a SendMessageinternally, it also does a PostMessage. The difference is that it waits till the delegate is executed on the UI thread before returning. So while there is a chance for the deadlock problem to occur, you can be sure that the other problems associated with SendMessage won't happen.Invoke returns the value that the function wrapped by the delegate returned.