如何使用 Visual C# .NET 或 Visual C# 2005 在多執行緒環境中同步化共用資源的存取

文章翻譯 文章翻譯
文章編號: 816161 - 檢視此文章適用的產品。
全部展開 | 全部摺疊

在此頁中

結論

您可以使用多執行緒在 Microsoft Visual C# .NET 或 Microsoft Visual C# 2005 應用程式中同時執行多項工作。多執行緒可同時啟動不同的執行緒以完成不同的工作。多執行緒也可以改善應用程式的效能和回應。

由於多個執行緒可以同時存取資源,因此您可能會想要同步處理個別執行緒與程式的其他部分。本文將告訴您一些多執行緒程式設計會遇到的共同狀況,並說明如何同步處理多個執行緒之間對共享資源的存取。

在多執行緒環境協助保護模組中的全域資料

應用程式中的所有執行緒都可以存取方法中的公用欄位。如果要同步化對公用欄位的存取,您可以改用屬性,而不要使用欄位,並且使用 ReaderWriterLock 物件來控制存取。如果要執行這項操作,請依照下列步驟執行:
  1. 啟動 Microsoft Visual Studio .NET 或 Microsoft Visual Studio 2005。
  2. [檔案] 功能表上,指向 [新增],然後按一下 [專案]
  3. 按一下 [專案類型] 下的 [Visual C# 專案],然後按一下 [範本] 下的 [主控台應用程式]

    注意 在 Visual Studio 2005 中,按一下 [專案類型] 下的 [Visual C#],然後按一下 [範本] 下的 [主控台應用程式]
  4. [名稱] 文字方塊中,輸入 MultiThreadApplication,然後按一下 [確定]
  5. 以 Class1.cs 中的下列程式碼取代現有的程式碼。

    注意 在 Visual Studio 2005 中,預設檔案為 Program.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# 的關鍵字鎖定陳述式表示,允許單一執行緒獨佔物件的執行權。下列範例步驟示範鎖定:
  1. 開啟 Visual Studio .NET 或 Visual Studio 2005。
  2. [檔案] 功能表上,指向 [新增],然後按一下 [專案]
  3. 按一下 [專案類型] 下的 [Visual C# 專案],然後按一下 [範本] 下的 [主控台應用程式]

    注意 在 Visual Studio 2005 中,按一下 [專案類型] 下的 [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. 建置專案,然後執行應用程式。

?考

如需詳細資訊,請參閱下列 Microsoft Developer Network (MSDN) 網站:
執行緒集區 (英文)
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://msdn2.microsoft.com/en-us/library/system.threading.readerwriterlock(vs.71).aspx

屬性

文章編號: 816161 - 上次校閱: 2007年6月1日 - 版次: 2.4
這篇文章中的資訊適用於:
  • Microsoft Visual C# 2005 Express Edition
  • Microsoft Visual C# .NET 2003 標準版
  • Microsoft Visual C# .NET 2002 Standard Edition
關鍵字:?
kbthreadsync kbthread kbhowtomaster KB816161
Microsoft及(或)其供應商不就任何在本伺服器上發表的文字資料及其相關圖表資訊的恰當性作任何承諾。所有文字資料及其相關圖表均以「現狀」供應,不負任何擔保責任。Microsoft及(或)其供應商謹此聲明,不負任何對與此資訊有關之擔保責任,包括關於適售性、適用於某一特定用途、權利或不侵權的明示或默示擔保責任。Microsoft及(或)其供應商無論如何不對因或與使用本伺服器上資訊或與資訊的實行有關而引起的契約、過失或其他侵權行為之訴訟中的特別的、間接的、衍生性的損害或任何因使用而喪失所導致的之損害、資料或利潤負任何責任。

提供意見

 

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