HOWTO: Visual C# .NET을 사용하여 다중 스레딩 환경에서 공유 리소스에 대한 액세스 동기화

기술 자료 번역 기술 자료 번역
기술 자료: 816161 - 이 문서가 적용되는 제품 보기.
모두 확대 | 모두 축소

이 페이지에서

요약

Visual C# .NET 응용 프로그램에서 다중 스레딩을 사용하면 동시에 여러 가지 작업을 수행할 수 있습니다. 다중 스레딩은 다른 스레드를 여러 개 시작하여 동시에 다른 작업을 수행할 수 있습니다. 다중 스레딩은 응용 프로그램의 성능과 응답 능력도 향상시킵니다.

여러 스레드가 한 리소스에 동시에 액세스할 수 있기 때문에 개별 스레드를 프로그램의 다른 부분과 동기화할 수 있습니다. 이 문서에서는 다중 스레딩 프로그래밍을 사용할 경우의 몇 가지 일반적인 시나리오를 설명하고, 여러 스레드 간에 공유 리소스에 대한 액세스를 동기화하는 방법을 설명합니다.

다중 스레드 환경에서 모듈의 전역 데이터를 보호하는 방법에 대한 도움말

메서드 내의 public 필드는 응용 프로그램 내의 모든 스레드가 액세스할 수 있습니다. public 필드에 대한 액세스를 동기화하는 데 필드 대신 속성을 사용하고 ReaderWriterLock 개체를 사용하여 액세스를 제어할 수 있습니다. 이렇게 하려면 다음과 같이 하십시오.
  1. Visual Studio .NET을 시작합니다.
  2. 파일 메뉴에서 새로 만들기를 가리킨 다음 프로젝트를 누릅니다.
  3. 프로젝트 형식에서 Visual C# 프로젝트를 누른 다음 템플릿에서 콘솔 응용 프로그램을 누릅니다.
  4. 이름 텍스트 상자에 MultiThreadApplication을 입력한 다음 확인을 누릅니다.
  5. 기존 코드를 Class1.cs의 다음 코드로 바꿉니다.
    using System;
    using System.Threading;
    
    namespace MultiThreadApplication
    {
    	class Class1
    	{
    		private ReaderWriterLock rwl = new ReaderWriterLock();
    		private long myNumber;
    		public long Number   // the Number property
    		{
    			get
    			{
    				//Acquire a read lock on the resource.
    				rwl.AcquireReaderLock(Timeout.Infinite);                
    				try
    				{
    					Console.WriteLine("Thread:{0} starts getting the Number", Thread.CurrentThread.GetHashCode());
    					Thread.Sleep(50);
    					Console.WriteLine("Thread:{0} got the Number", Thread.CurrentThread.GetHashCode());
    
    				}
    				finally
    				{
    					//Release the lock.
    					rwl.ReleaseReaderLock();
    				}
    				return myNumber;
    			}
    			set
    			{
    				//Acquire a write lock on the resource.
    				rwl.AcquireWriterLock(Timeout.Infinite);
    				try
    				{
    					Console.WriteLine("Thread: {0} start writing the Number", Thread.CurrentThread.GetHashCode());
    					Thread.Sleep(50);
    					myNumber = value;
    					Console.WriteLine("Thread: {0} written the Number", Thread.CurrentThread.GetHashCode());
    				}
    				finally
    				{
    					//Release the lock.
    					rwl.ReleaseWriterLock();
    				}
    			}
    		}
    		
    		[STAThread]
    		static void Main(string[] args)
    		{
    			Thread []threadArray = new Thread[20]; 
    			int threadNum;
    
    
    			Class1 Myclass = new Class1();
    			ThreadStart myThreadStart = new ThreadStart(Myclass.AccessGlobalResource);
    
    			//Create 20 threads.
    			for( threadNum = 0; threadNum < 20; threadNum++)
    			{
    				threadArray[threadNum] = new Thread(myThreadStart);
    			}
    
    			//Start the threads.
    			for( threadNum = 0; threadNum < 20; threadNum++)
    			{   
    				threadArray[threadNum].Start();
    			}
    
    			//Wait until all the thread spawn out finish.
    			for( threadNum = 0; threadNum < 20; threadNum++)
    				threadArray[threadNum].Join();
    
    			Console.WriteLine("All operations have completed. Press enter to exit");
    			Console.ReadLine();
    		}
    
    		public void AccessGlobalResource()
    		{
    			Random rnd = new Random();
    			long theNumber;
    			
    			if (rnd.Next() % 2 != 0)
    				theNumber = Number;
    			else
    			{
    				theNumber = rnd.Next();
    				Number = theNumber;
    			}
    
    		}
    	}
    }
    
  6. 프로젝트를 빌드한 다음 응용 프로그램을 실행합니다.

클래스를 스레드로부터 안전하게 만드는 방법에 대한 도움말

여러 스레드가 동시에 한 개체에 액세스하려고 할 수 있습니다. 둘 이상의 스레드가 동시에 한 개체에 액세스하려고 경합할 때 한 스레드가 리소스를 수정하면 동시에 액세스하던 다른 스레드가 올바르지 않은 상태를 받을 수 있습니다. 예를 들어, 한 스레드가 개체의 필드를 수정하는 동안 다른 스레드가 그 필드를 읽을 경우 첫 번째 스레드가 잘못된 필드 상태를 받을 수 있습니다. 이러한 상황을 경쟁 조건이라고 합니다.

이러한 상황을 피하려면 잠금을 사용하여 코드의 중요 구역을 경쟁 조건으로부터 보호하면 도움이 될 수 있습니다. Visual C# 키워드 lock 문으로 표현되는 잠금을 사용하면 단일 실행 스레드가 개체에 대해 배타적인 실행 권한을 얻을 수 있습니다. 다음 예제는 잠금을 설명합니다.
  1. Visual Studio .NET을 엽니다.
  2. 파일 메뉴에서 새로 만들기를 가리킨 다음 프로젝트를 누릅니다.
  3. 프로젝트 형식에서 Visual C# 프로젝트를 누른 다음 템플릿에서 콘솔 응용 프로그램을 누릅니다.
  4. 이름 텍스트 상자에 MultiThreadLockApplication을 입력한 다음 확인을 누릅니다.
  5. 기존 코드를 Class1.cs의 다음 코드로 바꿉니다.
    using System;
    using System.Threading;
    
    namespace MultiThreadLockApplication
    {
    	class Student
    	{
    		private static string myTeacherName = "Bill";
    		private string myName = "Grace";
    		private static object somePrivateStaticObject = new Object();
    
    		public static string TeacherName
    		{
    			get
    			{
    				string theName;
    
    				// Synchronize access to the shared member.
    				lock(somePrivateStaticObject)
    				{
    					Console.WriteLine("Thread {0} starts to get the teacher's name",Thread.CurrentThread.GetHashCode());
    					theName = myTeacherName;
    
    					// Wait for 0.3 second.
    					Thread.Sleep(300);
    					Console.WriteLine("Thread {0} finished to get the teacher's name:{1}.", Thread.CurrentThread.GetHashCode(), theName);
    				}
    				return theName;
    			}
    
    			set
    			{
    				lock(somePrivateStaticObject)
    				{
    					Console.WriteLine("Thread {0} starts to set the teacher's name.", Thread.CurrentThread.GetHashCode());
    					myTeacherName = value;
    
    					// Wait for 0.3 second.
    					Thread.Sleep(300);
    					Console.WriteLine("Thread {0} finished to set the teacher's name:{1}.", Thread.CurrentThread.GetHashCode(), value);
    				}
    			}
    		}
    
    		public string GetName()
    		{
    			string theName;
    			lock(this)
    			{
    				Console.WriteLine("Thread {0} starts to get the student's name.", Thread.CurrentThread.GetHashCode());
    				theName = myName;
    
    				// Wait for 0.3 second.
    				Thread.Sleep(300);
    				Console.WriteLine("Thread {0} finished to get the student's name:{1}", Thread.CurrentThread.GetHashCode(), theName);
    				return theName;
    			}
    		}
    
    		public string SetName(string NewName)
    		{
    			string theOldName;
    			lock(this)
    			{
    				Console.WriteLine("Thread {0} starts to set the student's name.", Thread.CurrentThread.GetHashCode());
    				theOldName = myName;
    				myName = NewName;
    
    				// Wait for 0.3 second.
    				Thread.Sleep(300);
    				Console.WriteLine("Thread {0} finished to set the student's name:{1}", Thread.CurrentThread.GetHashCode(), NewName);
    			}
    			return theOldName;
    		}
    	}
    
    	class Class1
    	{
    		public static int WorkItemNum = 20;
    		public static AutoResetEvent Done = new AutoResetEvent(false);
    
    		public static void AccessClassResource(object state)
    		{
    			Random rnd = new Random();
    			string theName;
    			Student AStudent = (Student) state;
    
    			if( (rnd.Next() %2) != 0)
    			{
    				if( (rnd.Next() %2) != 0)
    				{
    					switch (rnd.Next() %3 )
    					{
    						case 0:
    							Student.TeacherName = "Tom";
    							break;
    						case 1:
    							Student.TeacherName = "Mike";
    							break;
    						case 2:
    							Student.TeacherName = "John";
    							break;
    					}
    				}
    				else
    				{
    					theName = Student.TeacherName;
    				}
    			}
    			else
    			{
    				if( (rnd.Next() %2) != 0)
    				{
    					switch (rnd.Next() %3 )
    					{
    						case 0:
    							AStudent.SetName("Janet");
    							break;
    						case 1:
    							AStudent.SetName("David");
    							break;
    						case 2:
    							AStudent.SetName("Ben");
    							break;
    					}
    				}
    				else
    				{
    					theName = AStudent.GetName();
    				}
    			}
    
    			if(Interlocked.Decrement( ref WorkItemNum) == 0)
    			{
    				Done.Set();
    			}
    		}
    
    		[STAThread]
    		static void Main(string[] args)
    		{
    			int threadNum;
    			Student AStudent = new Student();
    
    			// Queue up 20 work items in the ThreadPool.
    			for (threadNum = 0 ; threadNum <= WorkItemNum -1 ; threadNum++) 
    			{
    				ThreadPool.QueueUserWorkItem(new WaitCallback(AccessClassResource),AStudent);
    			}
    
    			Done.WaitOne();
    			Console.WriteLine("All operations have completed. Press enter to exit");
    			Console.ReadLine();
    		}
    	}
    }
  6. 프로젝트를 빌드한 다음 응용 프로그램을 실행합니다.

참조

자세한 내용은 다음 MSDN(Microsoft Developer Network) 웹 사이트를 참조하십시오.
스레드 폴링
http://msdn.microsoft.com/library/en-us/cpguide/html/cpconthreadpooling.asp
ReaderWriterLock
http://msdn.microsoft.com/library/en-us/cpguide/html/cpconreaderwriterlock.asp
ReaderWriterLock 클래스
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemThreadingReaderWriterLockClassTopic.asp?frame=true




Microsoft 제품 관련 기술 전문가들과 온라인으로 정보를 교환하시려면 Microsoft 뉴스 그룹에 참여하시기 바랍니다.

속성

기술 자료: 816161 - 마지막 검토: 2003년 9월 18일 목요일 - 수정: 1.1
본 문서의 정보는 다음의 제품에 적용됩니다.
  • Microsoft Visual C# .NET 2003 Standard Edition
  • Microsoft Visual C# .NET 2002 Standard Edition
키워드:?
kbthreadsync kbthread kbhowtomaster KB816161

피드백 보내기

 

Contact us for more help

Contact us for more help
Connect with Answer Desk for expert help.
Get more support from smallbusiness.support.microsoft.com