Home CS(C#) 스레드로부터 안전한 방식으로 Windows Forms 컨트롤 호출
CS(C#)

스레드로부터 안전한 방식으로 Windows Forms 컨트롤 호출

스레드로부터 안전한 방식으로 Windows Forms 컨트롤 호출

Windows Forms 응용 프로그램의 성능을 개선하기 위해 다중 스레딩을 사용하는 경우에는 스레드로부터 안전한 방식으로 컨트롤을 호출할 수 있습니다.
Windows Forms 컨트롤에 대한 액세스는 기본적으로 스레드로부터 안전하지 않습니다. 둘 이상의 스레드가 컨트롤 상태를 조작하는 경우 컨트롤이 불일치하는 상태로 강제 지정될 수 있습니다. 또한 경합 상태와 교착 상태 등의 기타 스레드 관련 버그도 발생할 수 있습니다. 컨트롤에 액세스할 때는 스레드로부터 안전한 방식을 사용해야 합니다.
Invoke 메서드를 사용하지 않고 컨트롤을 만든 스레드가 아닌 다른 스레드에서 컨트롤을 호출하는 것은 안전하지 않습니다. 아래에는 스레드로부터 안전하지 않은 호출의 예제가 나와 있습니다.

		// This event handler creates a thread that calls a
		// Windows Forms control in an unsafe way.
		private void setTextUnsafeBtn_Click(
			object sender,
			EventArgs e)
		{
			this.demoThread =
				new Thread(new ThreadStart(this.ThreadProcUnsafe));
			this.demoThread.Start();
		}
		// This method is executed on the worker thread and makes
		// an unsafe call on the TextBox control.
		private void ThreadProcUnsafe()
		{
			this.textBox1.Text = "This text was set unsafely.";
		}

.NET Framework에서는 스레드로부터 안전하지 않은 방식으로 컨트롤에 액세스하는 경우를 검색할 수 있습니다. 디버거에서 응용 프로그램을 실행할 때 컨트롤을 만든 스레드가 아닌 다른 스레드가 해당 컨트롤을 호출하려고 하면 디버거에서 InvalidOperationException이 발생하고 “컨트롤 이름 컨트롤이 자신이 만들어진 스레드가 아닌 스레드에서 액세스되었습니다.” 메시지가 표시됩니다.
이 예외는 안전하지 않은 액세스를 안정적으로 확인할 수 있도록 디버깅 중에 발생하며 가끔 런타임에 발생하는 경우도 있습니다..NET Framework 이전 .NET Framework 2.0 버전을 사용하여 작성한 응용 프로그램을 디버그할 때 이 예외가 표시될 수 있습니다. 이 문제는 표시되는 경우 해결하는 것이 좋지만 CheckForIllegalCrossThreadCalls 속성을 false로 설정하면 비활성화할 수 있습니다. 이렇게 하면 컨트롤이 Visual Studio.NET 2003 및 .NET Framework 1.1에서 실행되는 것처럼 실행됩니다.

System_CAPS_ICON_note.jpg 참고
폼에서 ActiveX 컨트롤을 사용하는 경우 디버거에서 실행 시 크로스 스레드 InvalidOperationException이 발생할 수 있습니다. 이 예외가 발생하면 ActiveX 컨트롤이 다중 스레딩을 지원하지 않습니다. Windows Forms에서 ActiveX 컨트롤을 사용하는 방법에 대한 자세한 내용은 Windows Forms 및 관리되지 않는 응용 프로그램를 참조하세요. Visual Studio를 사용하는 경우 Visual Studio 호스팅 프로세스를 사용하지 않도록 설정하여 이 예외를 방지할 수 있습니다(방법: 호스팅 프로세스 비활성화 참조).

스레드로부터 안전한 방식으로 Windows Forms 컨트롤을 호출하려면

  1. 컨트롤의 InvokeRequired 속성을 쿼리합니다.
  2. InvokeRequiredtrue를 반환하면 실제 컨트롤 호출을 수행하는 대리자를 포함하여 Invoke를 호출합니다.
  3. InvokeRequiredfalse를 반환하면 컨트롤을 직접 호출합니다.

다음 코드 예제에서는 백그라운드 스레드를 통해 실행되는 ThreadProcSafe 메서드에서 스레드로부터 안전한 호출이 구현됩니다.TextBox 컨트롤의 InvokeRequiredtrue를 반환하면 ThreadProcSafe 메서드는 SetTextCallback 인스턴스를 만들어 폼의 Invoke 메서드로 전달합니다. 이 경우 SetText컨트롤을 만든 스레드에서 TextBox 메서드가 호출되며 이 스레드 컨텍스트에서 Text 속성이 직접 설정됩니다.

		// This event handler creates a thread that calls a
		// Windows Forms control in a thread-safe way.
		private void setTextSafeBtn_Click(
			object sender,
			EventArgs e)
		{
			this.demoThread =
				new Thread(new ThreadStart(this.ThreadProcSafe));
			this.demoThread.Start();
		}
		// This method is executed on the worker thread and makes
		// a thread-safe call on the TextBox control.
		private void ThreadProcSafe()
		{
			this.SetText("This text was set safely.");
		}
		// This delegate enables asynchronous calls for setting
		// the text property on a TextBox control.
		delegate void SetTextCallback(string text);
		// This method demonstrates a pattern for making thread-safe
		// calls on a Windows Forms control.
		//
		// If the calling thread is different from the thread that
		// created the TextBox control, this method creates a
		// SetTextCallback and calls itself asynchronously using the
		// Invoke method.
		//
		// If the calling thread is the same as the thread that created
		// the TextBox control, the Text property is set directly.
		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;
			}
		}

응용 프로그램에서 다중 스레딩을 구현하는 기본 방법은 BackgroundWorker 구성 요소를 사용하는 것입니다.BackgroundWorker 구성 요소는 다중 스레딩에 이벤트 구동 모델을 사용합니다. 백그라운드 스레드는 DoWork 이벤트 처리기를 실행하며 컨트롤을 만드는 스레드는 ProgressChangedRunWorkerCompleted 이벤트 처리기를 실행합니다.ProgressChangedRunWorkerCompleted 이벤트 처리기에서 컨트롤을 호출할 수 있습니다.

BackgroundWorker를 사용하여 스레드로부터 안전한 호출을 수행하려면

  1. 백그라운드 스레드에서 수행하려는 작업을 수행하는 메서드를 만듭니다. 이 메서드에서 Main 메서드가 만든 컨트롤을 호출하지 마세요.
  2. 백그라운드 작업이 완료된 후 해당 작업의 결과를 보고하는 메서드를 만듭니다. 이 메서드에서는 Main 메서드가 만든 컨트롤을 호출할 수 있습니다.
  3. 1단계에서 만든 메서드를 DoWork 인스턴스의 BackgroundWorker 이벤트에 바인딩하고 2단계에서 만든 메서드를 같은 인스턴스의 RunWorkerCompleted 이벤트에 바인딩합니다.
  4. 백그라운드 스레드를 시작하려면 RunWorkerAsync 인스턴스의 BackgroundWorker 메서드를 호출합니다.

다음 코드 예제에서 DoWork 이벤트 처리기는 Sleep를 사용하여 시간이 오래 걸리는 작업을 시뮬레이트합니다. 그러나 폼의 TextBox 컨트롤은 호출하지 않습니다.TextBox 컨트롤의 Text 속성은 RunWorkerCompleted 이벤트 처리기에서 직접 설정됩니다.

		// This BackgroundWorker is used to demonstrate the
		// preferred way of performing asynchronous operations.
		private BackgroundWorker backgroundWorker1;
		// This event handler starts the form's
		// BackgroundWorker by calling RunWorkerAsync.
		//
		// The Text property of the TextBox control is set
		// when the BackgroundWorker raises the RunWorkerCompleted
		// event.
		private void setTextBackgroundWorkerBtn_Click(
			object sender,
			EventArgs e)
		{
			this.backgroundWorker1.RunWorkerAsync();
		}
		// This event handler sets the Text property of the TextBox
		// control. It is called on the thread that created the
		// TextBox control, so the call is thread-safe.
		//
		// BackgroundWorker is the preferred way to perform asynchronous
		// operations.
		private void backgroundWorker1_RunWorkerCompleted(
			object sender,
			RunWorkerCompletedEventArgs e)
		{
			this.textBox1.Text =
				"This text was set safely by BackgroundWorker.";
		}

ProgressChanged 이벤트를 사용하여 백그라운드 작업의 진행률을 보고할 수도 있습니다. 해당 이벤트가 통합되어 있는 예제를 보려면 BackgroundWorker를 참조하세요.

다음 코드 예제는 단추 세 개와 텍스트 상자 하나가 포함된 폼으로 구성되는 완전한 Windows Forms 응용 프로그램입니다. 첫 번째 단추는 안전하지 않은 크로스 스레드 액세스를, 두 번째 단추는 Invoke를 사용하는 안전한 액세스를, 세 번째 단추는 BackgroundWorker를 사용하는 안전한 액세스를 보여 줍니다.

System_CAPS_ICON_note.jpg 참고
예제를 실행하는 방법에 대한 지침은 How to: Compile and Run a Complete Windows Forms Code Example Using Visual Studio를 참조하세요. 이 예제를 실행하려면 System.Drawing 및 System.Windows.Forms 어셈블리에 대한 참조가 필요합니다.
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
namespace CrossThreadDemo
{
	public class Form1 : Form
	{
		// This delegate enables asynchronous calls for setting
		// the text property on a TextBox control.
		delegate void SetTextCallback(string text);
		// This thread is used to demonstrate both thread-safe and
		// unsafe ways to call a Windows Forms control.
		private Thread demoThread = null;
		// This BackgroundWorker is used to demonstrate the
		// preferred way of performing asynchronous operations.
		private BackgroundWorker backgroundWorker1;
		private TextBox textBox1;
		private Button setTextUnsafeBtn;
		private Button setTextSafeBtn;
		private Button setTextBackgroundWorkerBtn;
		private System.ComponentModel.IContainer components = null;
		public Form1()
		{
			InitializeComponent();
		}
		protected override void Dispose(bool disposing)
		{
			if (disposing && (components != null))
			{
				components.Dispose();
			}
			base.Dispose(disposing);
		}
		// This event handler creates a thread that calls a
		// Windows Forms control in an unsafe way.
		private void setTextUnsafeBtn_Click(
			object sender,
			EventArgs e)
		{
			this.demoThread =
				new Thread(new ThreadStart(this.ThreadProcUnsafe));
			this.demoThread.Start();
		}
		// This method is executed on the worker thread and makes
		// an unsafe call on the TextBox control.
		private void ThreadProcUnsafe()
		{
			this.textBox1.Text = "This text was set unsafely.";
		}
		// This event handler creates a thread that calls a
		// Windows Forms control in a thread-safe way.
		private void setTextSafeBtn_Click(
			object sender,
			EventArgs e)
		{
			this.demoThread =
				new Thread(new ThreadStart(this.ThreadProcSafe));
			this.demoThread.Start();
		}
		// This method is executed on the worker thread and makes
		// a thread-safe call on the TextBox control.
		private void ThreadProcSafe()
		{
			this.SetText("This text was set safely.");
		}
		// This method demonstrates a pattern for making thread-safe
		// calls on a Windows Forms control.
		//
		// If the calling thread is different from the thread that
		// created the TextBox control, this method creates a
		// SetTextCallback and calls itself asynchronously using the
		// Invoke method.
		//
		// If the calling thread is the same as the thread that created
		// the TextBox control, the Text property is set directly.
		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 event handler starts the form's
		// BackgroundWorker by calling RunWorkerAsync.
		//
		// The Text property of the TextBox control is set
		// when the BackgroundWorker raises the RunWorkerCompleted
		// event.
		private void setTextBackgroundWorkerBtn_Click(
			object sender,
			EventArgs e)
		{
			this.backgroundWorker1.RunWorkerAsync();
		}
		// This event handler sets the Text property of the TextBox
		// control. It is called on the thread that created the
		// TextBox control, so the call is thread-safe.
		//
		// BackgroundWorker is the preferred way to perform asynchronous
		// operations.
		private void backgroundWorker1_RunWorkerCompleted(
			object sender,
			RunWorkerCompletedEventArgs e)
		{
			this.textBox1.Text =
				"This text was set safely by BackgroundWorker.";
		}
		#region Windows Form Designer generated code
		private void InitializeComponent()
		{
			this.textBox1 = new System.Windows.Forms.TextBox();
			this.setTextUnsafeBtn = new System.Windows.Forms.Button();
			this.setTextSafeBtn = new System.Windows.Forms.Button();
			this.setTextBackgroundWorkerBtn = new System.Windows.Forms.Button();
			this.backgroundWorker1 = new System.ComponentModel.BackgroundWorker();
			this.SuspendLayout();
			//
			// textBox1
			//
			this.textBox1.Location = new System.Drawing.Point(12, 12);
			this.textBox1.Name = "textBox1";
			this.textBox1.Size = new System.Drawing.Size(240, 20);
			this.textBox1.TabIndex = 0;
			//
			// setTextUnsafeBtn
			//
			this.setTextUnsafeBtn.Location = new System.Drawing.Point(15, 55);
			this.setTextUnsafeBtn.Name = "setTextUnsafeBtn";
			this.setTextUnsafeBtn.TabIndex = 1;
			this.setTextUnsafeBtn.Text = "Unsafe Call";
			this.setTextUnsafeBtn.Click += new System.EventHandler(this.setTextUnsafeBtn_Click);
			//
			// setTextSafeBtn
			//
			this.setTextSafeBtn.Location = new System.Drawing.Point(96, 55);
			this.setTextSafeBtn.Name = "setTextSafeBtn";
			this.setTextSafeBtn.TabIndex = 2;
			this.setTextSafeBtn.Text = "Safe Call";
			this.setTextSafeBtn.Click += new System.EventHandler(this.setTextSafeBtn_Click);
			//
			// setTextBackgroundWorkerBtn
			//
			this.setTextBackgroundWorkerBtn.Location = new System.Drawing.Point(177, 55);
			this.setTextBackgroundWorkerBtn.Name = "setTextBackgroundWorkerBtn";
			this.setTextBackgroundWorkerBtn.TabIndex = 3;
			this.setTextBackgroundWorkerBtn.Text = "Safe BW Call";
			this.setTextBackgroundWorkerBtn.Click += new System.EventHandler(this.setTextBackgroundWorkerBtn_Click);
			//
			// backgroundWorker1
			//
			this.backgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerCompleted);
			//
			// Form1
			//
			this.ClientSize = new System.Drawing.Size(268, 96);
			this.Controls.Add(this.setTextBackgroundWorkerBtn);
			this.Controls.Add(this.setTextSafeBtn);
			this.Controls.Add(this.setTextUnsafeBtn);
			this.Controls.Add(this.textBox1);
			this.Name = "Form1";
			this.Text = "Form1";
			this.ResumeLayout(false);
			this.PerformLayout();
		}
		#endregion
		[STAThread]
		static void Main()
		{
			Application.EnableVisualStyles();
			Application.Run(new Form1());
		}
	}
}

응용 프로그램을 실행하고 Unsafe Call 단추를 클릭하는 즉시 텍스트 상자에 “Written by the main thread”가 표시됩니다. 그리고 2초 후에 안전하지 않은 호출을 시도하면 Visual Studio 디버거에 예외가 발생했음이 표시됩니다. 그러면 디버거가 텍스트 상자에 직접 쓰기를 시도한 백그라운드 스레드의 줄에서 중지됩니다. 나머지 두 단추를 테스트하려면 응용 프로그램을 다시 시작해야 합니다.Safe Call 단추를 클릭하면 텍스트 상자에 “Written by the main thread”가 표시되고, 2초 후에 텍스트 상자는 Invoke 메서드가 호출되었음을 나타내는 “Written by the background thread (Invoke)”로 설정됩니다.Safe BW Call 단추를 클릭하면 텍스트 상자에 “Written by the main thread”가 표시되고, 2초 후에 텍스트 상자는 RunWorkerCompletedBackgroundWorker 이벤트에 대한 처리기가 호출되었음을 나타내는 “Written by the main thread after the background thread completed”가 표시됩니다.

Author

admin

Join the Conversation

  1. I simply want to mention I’m all new to blogging and site-building and certainly enjoyed this blog site. Probably I’m planning to bookmark your site . You certainly have excellent well written articles. Appreciate it for revealing your website.

  2. Nice post. I learn something more challenging on distinct blogs everyday. It will always be stimulating to read content off their writers and practice a little something from their store. I’d choose to use some with all the content in my small weblog whether you do not mind. Natually I’ll provide a link on your own internet weblog. Many thanks sharing.

  3. Hi there just wanted to give you a quick heads up and let you know a few of the pictures aren’t loading properly. I’m not sure why but I think its a linking issue. I’ve tried it in two different web browsers and both show the same outcome.

  4. Aw, this was an actually wonderful article. In suggestion I wish to place in writing similar to this in addition? taking some time and actual effort to make an excellent short article? but what can I say? I postpone alot and also by no means appear to get something done.

  5. The next time I read a blog site, I wish that it doesn’t dissatisfy me as long as this one. I indicate, I understand it was my option to check out, but I actually assumed youd have something intriguing to say. All I listen to is a bunch of whimpering regarding something that you might take care of if you werent as well active searching for interest.

  6. An interesting conversation is worth remark. I think that you should create much more on this subject, it may not be a taboo topic yet usually people are insufficient to speak on such topics. To the next. Thanks

  7. This is the appropriate blog for any person that wishes to find out about this subject. You understand so much its virtually difficult to suggest with you (not that I really would want?HaHa). You absolutely put a new spin on a subject thats been covered for years. Great stuff, simply fantastic!

  8. The next time I check out a blog, I really hope that it does not dissatisfy me as high as this. I mean, I recognize it was my option to check out, however I really believed youd have something interesting to say. All I hear is a bunch of whining about something that you might fix if you werent also busy looking for attention.

  9. Aw, this was a really wonderful blog post. In suggestion I would love to place in composing similar to this additionally? requiring time and also actual initiative to make a very good post? but what can I say? I postpone alot and also by no means seem to obtain something done.

  10. Good post. I learn something extra challenging on different blogs day-to-day. It will certainly always be boosting to review material from other authors as well as exercise a little something from their store. I?d like to utilize some with the web content on my blog whether you don?t mind. Natually I?ll offer you a web link on your internet blog. Many thanks for sharing.

  11. Can I simply claim what an alleviation to locate someone who actually recognizes what theyre talking about on the web. You most definitely recognize just how to bring a problem to light and also make it essential. Even more people need to read this and also comprehend this side of the story. I angle believe youre not a lot more popular because you most definitely have the gift.

  12. When I originally commented I clicked the -Notify me when new comments are added- checkbox as well as now each time a remark is included I obtain four e-mails with the very same remark. Is there any way you can eliminate me from that service? Thanks!

  13. This is the right blog site for anybody who wishes to discover this subject. You understand a lot its almost tough to say with you (not that I actually would want?HaHa). You definitely put a new spin on a subject thats been discussed for many years. Wonderful stuff, simply fantastic!

  14. Oh my goodness! an incredible article man. Thanks Nevertheless I am experiencing problem with ur rss. Don?t know why Not able to register for it. Exists anyone getting the same rss issue? Anyone who understands kindly respond. Thnkx

댓글 남기기

이메일은 공개되지 않습니다.