線程基礎
一個進程由若干個線程組成,線程是程序執行的基本原子單位。線程是"進程"中某個單一順序的控制流,線程是進程中的一個基本執行流,每個線程都有自己專屬的寄存器(程序計數器、棧指針等),代碼共享區,不同的線程可以執行同樣的方法。
多線程可以實現并行處理,可以避免某項任務長時間占用CPU時間,需要注意的是,多線程程序對于效率,應該根據任務不同的要求來選擇。
線程的命名空間System.Threading
Thread類是線程中最重要的一個,Thread類提供了創建并控制線程,設置其優先級并獲取其狀態的方法。
Thread類的聲明:
class ThreadSimple
{
//靜態線程函數
public static void ThreadMethodExample()
{
}
}
//調用靜態方法
Thread threadSimple = new Thread(ThreadSimple.ThreadMethodExample);
或:
class ThreadSimple
{
//靜態線程函數
public static void ThreadMethodExample()
{
}
//調用靜態方法
//Thread threadSimple = new Thread(ThreadSimple.ThreadMethodExample);
Thread threadSimple = new Thread(new ThreadStart(ThreadMethodExample));
}
-
線程啟動、結束
Thread類的常用方法:
- IsAlive:判斷線程是否處于活動狀態
- Name:線程的名稱
- Priority:ThreadPriority枚舉類型,代表線程的優先級{Normal,AboveNormal,BelowNormal,Highest,Lowest}
- ThreadState:ThreadState枚舉類型,代表線程的狀態 { Running(線程已啟動,正在執行) , StopRequested(正在請求停止此線程), SuspendRequested(正在請求掛起此線程), Background(線程正在被作為后臺線程執行), Unstarted(尚未啟動線程),Stopped(線程已經停止),WaitSleepJoin(線程已經被阻止),Suspended(線程已經掛起),AbortRequested(對線程調用了Thread.Abort()方法但是線程尚未收到試圖終止它的掛起的System.Threading.ThreadAbortException) , Aborted(線程狀態包括AbortRequested,并且該線程現在已死,但其狀態尚未更改為Stopped) }
- Start:啟動一個線程
- Suspend:掛起一個線程的運行(暫停、中斷)
- Resume:繼續被掛起的線程,恢復被Suspend()方法掛起的線程的執行
- Abort():結束一個線程的運行,終止線程
- Sleep():線程的休眠,使線程進入一定時間的休眠狀態,時間一到,線程繼續執行
-
線程間數據同步
-
線程間數據共享
多線程編程中,如果線程間需要共享數據,需要把共享的數據設置為靜態類型的,此時可以使用static關鍵字
-
Lock語句同步數據訪問
線程之間的同步和通信處理,兩個線程需要同時操作一個隊列,一個線程進行添加操作,另一個線程進行取用元素操作。
lock關鍵字將語句塊標記為臨界區,方法是獲取給定對象的互斥鎖,執行語句,然后釋放該鎖。Lock的語法:lock(expression)statement_block【其中expression是加鎖對象,必須是引用類型,不能是數值類型,statement_block代表正在訪問的共享資源的程序段】,
lock語句可以很好地實現互斥操作,從而保護數據在某個時刻內只有一個線程可以操作該數據,直至操作完成才允許其他線程進行操作,這樣就實現了按順序操作的設計,從而避免不可預料的情況發生。
-
public static void MethodSubB()
{
do
{
lock(lockExample)
{
i-=1;
Console.WriteLine("線程2開始,共享數據為:i={0}",i);
Thread.Sleep(2000); //線程A休眠2秒
Console.WriteLine("線程2結束,共享數據值i={0}",i);
}
} while (true);
}
線程可以使用Mutex.WaitOne()方法釋放這個對象,而在此期間,其他想要獲取這個Mutex對象的線程都只能等待。
Monitor類用于鎖定對象,一個線程只有得到這把鎖才能對該對象進行操作,對象鎖保證了在可能引起混亂的情況下,一個時刻只有一個線程可以訪問這個對象。
Monitor必須和一個具體的對象相關聯,但由于它是一個靜態類,所以不能使用它來定義對象,而且它的所有方法都是靜態的,不能使用對象來引用。
當一個線程調用Monitor.Enter()方法鎖定對象時,這個對象就歸它所有了,其他線程想要訪問這個對象,只有等待它調用Monitor.Exit()方法釋放鎖。
Monitor類主要成員
Enter |
在指定對象上獲取排他鎖 |
Exit |
釋放指定對象上的排它鎖 |
Pluse |
通知等待隊列中的線程鎖定對象狀態的更改 |
PluseAll |
通知所有的等待線程對象狀態的更改 |
TryEnter |
試圖獲取指定對象上的排他鎖 |
Wait |
釋放對象上的鎖并阻止當前線程,直到它重新獲取該鎖 |
private static Object sObjectA = new Object();
private static Object sObjectB = new Object();
public static void DemoA()
{
if(Monitor.TryEnter(sObjectA,1000))
{
Thread.Sleep(1000);
if (Monitor.TryEnter(sObjectB, 2000))
{
Monitor.Exit(sObjectB);
}
else
{
Console.WriteLine("TryEnter SObjectB超時...");
}
Monitor.Exit(sObjectA);
}
Console.WriteLine("執行DemoA");
}
public static void DemoB()
{
if (Monitor.TryEnter(sObjectB, 1000))
{
Thread.Sleep(1000);
if (Monitor.TryEnter(sObjectA, 2000))
{
Monitor.Exit(sObjectA);
}
else
{
Console.WriteLine("TryEnter SObjectA超時...");
}
Monitor.Exit(sObjectB);
}
Console.WriteLine("執行DemoB");
}
static void Main(string[] args)
{
Thread threadA = new Thread(DemoA);
Thread threadB = new Thread(DemoB);
threadA.Start();
threadB.Start();
Thread.Sleep(4000);
Console.WriteLine("線程結束");
}
5.帶參數線程
在不傳遞參數的情況下,可以使用ThreadStart代理來執行函數,如果要傳遞參數給執行函數,則可使用ParameterizedThreadStart代理來鏈接函數
Thread類的4個重載的構造函數
1.Thread(ThreadStart)[初始化Thread類的新實例]
2.Thread(ParameterizedThreadStart)
[初始化Thread類的新實例,指定允許對象在線程啟動時傳遞給線程的委托]
3.Thread(ParameterizedThreadStart,Int32)
4.Thread(ThreadStart,Int32)
[初始化Thread類的新實例,并指定線程的最大堆棧]
實例:
class ThreadDemo
{
public int paraA, paraB;
public void MethodDemo()
{
Console.WriteLine("paraA={0},paraB={1}", paraA, paraB);
}
public void Print(object obj)
{
Console.WriteLine("傳入的參數是{0}", obj.ToString());
}
}
static void Main(string[] args)
{
ThreadDemo A = new ThreadDemo();
A.paraA = 2;
A.paraB = 3;
Thread threadA = new Thread(new ThreadStart(A.MethodDemo));
threadA.Start();
Thread threadB = new Thread(new ParameterizedThreadStart(new ThreadDemo().Print));
threadB.Start(" 這是傳入的參數");
Console.Write("Press any key to continue...");
Console.ReadKey();
}
6.線程池ThreadPool
線程池是可以在后臺執行多個任務的線程集合,這使得主線程可以自由地異步執行其他任務。一旦池中的某個線程任務完成,它將返回到等待線程隊列中等待在此被使用。線程池線程都是后臺線程。
以下情況應該使用單獨的線程不宜使用線程池
1.線程需要指定優先級
2.線程執行的時間較長
3.線程在單獨的線程apartment中
4.在線程執行的過程中對線程存在操作
static void MethodA(object num)
{
int QueueNum = (int)num;
Console.WriteLine("線程號:{0}", QueueNum);
//輸出空行,為了美觀
Console.WriteLine();
}
static void Main(string[] args)
{
for (int i = 0; i < 5; i++)
{
//在線程池中創建線程池線程來執行指定的方法(用WaitCallBack來表示),并將此線程排入線程池的隊列等待執行
ThreadPool.QueueUserWorkItem(new WaitCallback(MethodA), i);
}
}
文章列表