1.使用線程的情況
①.程式需要執行和兩個和多個任務
②.程式要等待某事件的發生:例如使用者輸入、檔案操作、網路操作和搜尋
③.背景程式
2.多線程的並發執行
如果有多個線程在執行,單CPU只有一個,到底執行的哪個?
①.如果一個線程連續佔用CPU資源時間過長,其它的資源得不到執行,
則系統會強制的切換執行其它線程。(強制剝奪)
②.如果一個線程沒事可做、CPU可執行其它線程。(主動放棄)
③.這是由作業系統的調度機制決定的,不同的作業系統調度機制不一樣。
一般無法精確的預料多線程的執行順序,在程式設計的時候應特別注意
3.建立並啟動線程
ThreadStart 線程啟動委託名=new ThreadStart(方法名);
Thread 線程執行個體名=new Thread(線程啟動委託名);
線程執行個體名.Start();
4.終止線程
①.線程執行個體名.Abort();用此方法的後果是不可恢複的終止線程。
②.線程執行個體名.Interrupt();中斷後可恢複
5.休眠線程
①.線程執行個體名.Sleep();
當線程Sleep時,系統就立即退出執行隊列一段時間,當睡眠結束時,系統會產生一個時鐘中斷,從而
使線程回到執行隊列中,從而恢複線程的執行。
6.掛起/恢複線程
①.線程執行個體名.Suspend();掛起
與線程休眠不同,線程的掛起不會使線程立即停止執行,直到線程到達安全點之後它才可以將該
線程掛起,如果線程尚未啟動或已經停止,則它將不能掛起。
②.線程執行個體名.Resume();恢複
將使一個線程跳出掛起狀態並使該線程繼續執行。
一個線程不能對另一個線程調用Sleep() ,但是一個線程可以對另一個線程調用Suspend()。
還可以使用許多其它的方式來阻塞線程。例如,可以通過調用 Thread.Join 使一個線程等待另一個線程 (子線程)停止。使用Monitor.Wait使一個線程等待訪問一個同步對象。
7.序列化線程
①.線程執行個體名.jion();
例如在主線程中插入t.jion();
主線程執行到這條語句後,主線程(當前線程)立即進入阻塞狀態.直到t運行完後阻塞狀態才解除。
相當於把t的任務插入或串聯到主線程中,把兩條線索串聯成一條線索
8.線程的鎖定機制
線程的鎖定機制可以保證每次只有一個線程可以訪問共用資源。
使用關鍵字lock
①.lock語句的文法
lock(對象引用)語句塊;
②.lock語句的功能
當對象被lock 鎖定時,訪問該線程的其它線程會進入等待的狀態。
③.對象鎖機制保證了對象訪問的完整性:只有一個線程完成操作後,其它的線程才能進行操作。
④.一般情況下,當一個線程寫某個變數,而同時可能有其它的線程讀或寫這個變數時,為了保持資料的一
致性應該使用鎖定機制。
⑤.線程的安全性
執行緒安全性就是保護的類的成員和代碼的安全,從而使他們不會同時被幾個線程中斷,使用鎖定機制。
⑥.多線程公用一個對象時,就不應該使用lock關鍵字了,這裡Monitor,Monitor提供了使線程共用資源的方 案。
Monitor類可以鎖定一個對象,一個線程只有得到這把鎖才可以對該對象進行操作。
如:
Monitor.Enter(obj);
Monitor.Exit(obj);
⑦.臨界區和鎖
當談論多線程應用程式的時候,首先應該想到的就是並發性問題。儘管這對於同時執行多個任務的程式是很有用的,但通常都是危險的。為瞭解決這個問題,在C#中提出了臨界區和鎖的概念。在程式設計中,臨界區是一塊在任何時候只能有一個進程進入的地區。在C#中通過語句lock來聲明臨界區。lock聲明後面的代碼,不管是以行還是一塊代碼,在同一時間最多隻能有一個進程執行。
9.線程的優先順序具有不可靠性,就是說不能用優先順序來控制線程的執行順序。
10.後台線程
①.什麼是後台線程?比起應用程式的主圖形化使用者介面(GUI)線程來說,這些線程以較低的優先權在不同的過程中
運行著。對於不能立即執行結束, 又不想一直等待的任務,後台線程能很好的勝任。在C#中,把線程對象的
IsBackground屬性設為true,該線程即為後台線程。
後台線程跟前台線程只有一個區別,那就是後台線程不妨礙程式的終止。一旦一個進程所有的前台線程都終止後,
CLR將通過調用任意一個存活中的後台進程的Abort()方法來徹底終止進程。
注意:後台線程不能直接操作所在進程之外的資料引用。
②.怎樣與後台線程通訊?運用MethodInvoker委派實體。
要使用MethodInvoker委派,需要三個條件:
a.一個建立委派的後台線程
Thread thread=new Thread(new ThreadStart(Run));
thread.IsBackground=true;//把Thread設為後台線程
thread.Start();
b.一個用作後台線程與前台可視化單元的介面的類級方法
public void Run()
{
int count=0;
try
{
MethodInvoker mi=new MethodInvoker(this.UpdateLabel);
//建立一個委託,UpdateLabel是該委託所託管的代碼,必須是聲明為void 且不接受任何參數的任何方法。
while(true)
{
count++;
//this.Invoke(mi);//同步執行委託
this.BeginInvoke(mi);//非同步執行委託
Thread.Sleep(500);
}
}
catch(ThreadInterruptedException e)
{
Console.WriteLine("Interruption Exception in Thread:{0}",e);
}
catch(Exception ex)
{
Console.WriteLine("Exception in Thread:{0}",ex);
}
}
c.一個應用程式中可以更新的可視化單元
public void UpdateLabel()
{
label1.Text=count.ToString();
}
例1:使用多線程實現的打字練習(VS2005)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
/*
* 編寫一個基於Windows表單的應用程式,實現打字練習功能,要使用多線程,在主線程裡用
* Timer控制項定時產生Label控制項,每個Label控制項顯示一個隨機產生的字母,每產生一個Label
* 就新開一個線程(輔助線程),這個線程用於控制Label控制項的向下移動,主線程監視鍵盤
* 輸入,如果鍵盤輸入文本與這個Label控制項的Text相同,Label就消失
*/
namespace TypewriteExcercise
{
public partial class frmTyped : Form
{
public frmTyped()
{
InitializeComponent();
}
private void frmTyped_Load(object sender, EventArgs e)
{
timer1.Start();//啟動時鐘
}
private void timer1_Tick(object sender, EventArgs e)
{
Label label = new Label();//建立標籤對象
label.Width = 12;
label.Height = 12;
label.ForeColor = Color.White;//把標籤的前景色彩設為白色
label.BackColor = this.BackColor;//把表單的背景色設定黑色為標籤的背景色
this.Controls.Add(label);//把標籤添加到表單中
System.Random random = new Random(DateTime.Now.Second * DateTime.Now.Second);
//當前系統時間的秒數的平方作為隨機種子
label.Left = random.Next(this.Width);//標籤隨機數[0,表單的寬度),
Letter letter = new Letter(label, this);//把標籤和表單傳遞給letter對象
ThreadStart threadstart = new ThreadStart(letter.Run);//建立線程啟動委託,注意括弧裡面是方法名
Thread thread = new Thread(threadstart);//建立線程執行個體
thread.Start();//啟動線程
}
private void frmTyped_KeyPress(object sender, KeyPressEventArgs e)
{
foreach(Label label in this.Controls)
{
if(label==null)//沒有標籤
{
break;//退出
}
if(label.Text[0]==e.KeyChar)
//label.Text返回的是字串,label.Text[0]返回第一個字元。e.KeyChar返回的是字元。這樣都為字元才能判斷
{
label.Dispose();//釋放由label所使用的所有資源
this.Controls.Remove(label);//從frmTyped中移除label
}
}
}
}
public class Letter
{
private Label _label;
private Form _container;
private int _speed = 2;
public Letter(Label label, Form container) //建構函式接受標籤和表單
{
_label = label;//初始化標籤
_container = container;//初始化表單
Random random = new Random(DateTime.Now.Second);//使用當前系統時間的秒數作為隨機種子
_speed = random.Next(5) + 1;//產生一個[1,6)的隨機數用來表示速度
_label.Text = Convert.ToChar(65 + random.Next(57)).ToString();
/*
* a-z的ASCII碼97-122 A-Z 的ASCII碼65-90 [:91 /:92 ]:93 ^:94 _:95 `:96
*/
switch(_speed)
{
case 1:
_label.ForeColor = Color.Red;//將標籤的前景色彩設為紅色
break;
case 2:
_label.ForeColor = Color.Yellow;
break;
case 3:
_label.ForeColor = Color.Blue;
break;
case 4:
_label.ForeColor = Color.Green;
break;
case 5:
_label.ForeColor = Color.White;
break;
default:
_label.ForeColor = Color.White;
break;
}
}
public void Run()
{
try
{
while (_label.Top <= this._container.Height + 100)//標籤的的上邊距小於或等於表單的高度+100像素
{
if (_label == null)//沒有產生標籤這種情況
{
Thread.CurrentThread.Abort();//就終止當前的線程
}
_label.Top += 1;//有標籤,上邊距就加1像素;
Thread.Sleep(_speed * 5);//讓線程休眠(速度越快,休眠的時間越短。以毫秒為單位);
}
if(Thread.CurrentThread.IsAlive)//如果當前線程還是存活的
{
Thread.CurrentThread.Abort();//就終止當前的線程
}
}
catch (Exception ex)
{
Console.WriteLine("錯誤:" + ex.Message);//擷取描述當前異常的訊息
Console.WriteLine("錯誤:" + ex.StackTrace);//擷取當前異常發生時呼叫堆疊上的幀的字串表達形式
}
finally //釋放資源,不管是否發生異常,finally都要五條件的執行
{
if(!_label.Disposing)//如果標籤沒有釋放到進程中
{
_label.Dispose();//釋放由標籤使用的所有資源
}
_container.Controls.Remove(_label);//移除在表單中產生的所有標籤
}
}
}
}