淺解關(guān)于C#多線程的介紹
多線程的相關(guān)概念
--------------------------------------------------------------------------------
1.進(jìn)程:是操作系統(tǒng)結(jié)構(gòu)的基礎(chǔ);是一個正在執(zhí)行的程序;計(jì)算機(jī)中正在運(yùn)行的程序?qū)嵗豢梢苑峙浣o處理器并由處理器執(zhí)行的一個實(shí)體;由單一順序的執(zhí)行顯示,一個當(dāng)前狀態(tài)和一組相關(guān)的系統(tǒng)資源所描述的活動單元。
2.線程:線程是程序中一個單一的順序控制流程。是程序執(zhí)行流的最小單元。另外,線程是進(jìn)程中的一個實(shí)體,是被系統(tǒng)獨(dú)立調(diào)度和分派的基本單位,線程自己不擁有系統(tǒng)資源,只擁有一點(diǎn)在運(yùn)行中必不可少的資源,但它可與同屬一個進(jìn)程的其它線程共享進(jìn)程所擁有的全部資源。一個線程可以創(chuàng)建和撤消另一個線程,同一進(jìn)程中的多個線程之間可以并發(fā)執(zhí)行。由于線程之間的相互制約,致使線程在運(yùn)行中呈現(xiàn)出間斷性。線程也有就緒、阻塞和運(yùn)行三種基本狀態(tài)。每一個程序都至少有一個線程,若程序只有一個線程,那就是程序本身。
3.多線程:在單個程序中同時運(yùn)行多個線程完成不同的工作,稱為多線程。
--------------------------------------------------------------------------------
小結(jié):其實(shí)更容易理解一點(diǎn)進(jìn)程與線程的話,可以舉這樣一個例子:把進(jìn)程理解成為一個運(yùn)營著的公司,然而每一個公司員工就可以叫做一個進(jìn)程。每個公司至少要有一個員工,員工越多,如果你的管理合理的話,公司的運(yùn)營速度就會越好。這里官味一點(diǎn)話就是說。cpu大部分時間處于空閑時間,浪費(fèi)了cpu資源,多線程可以讓一個程序“同時”處理多個事情,提高效率。
--------------------------------------------------------------------------------
單線程問題演示
--------------------------------------------------------------------------------
創(chuàng)建一個WinForm應(yīng)用程序,這里出現(xiàn)的問題是,點(diǎn)擊按鈕后如果在彈出提示框之前,窗體是不能被拖動的。
private void button1_Click(object sender, EventArgs e)
{
for (int i = 0; i < 10000000000; i++)
{
i += 1;
}
MessageBox.Show("出現(xiàn)后能拖動,提示沒出現(xiàn)之前窗體不能被拖動");
}
原因:運(yùn)行這個應(yīng)用程序的時候,窗體應(yīng)用程序自帶一個叫做UI的線程,這個線程負(fù)責(zé)窗體界面的移動大小等。如果點(diǎn)擊按鈕則這個線程就去處理這個循環(huán)計(jì)算,而放棄了其它操作,故而窗體拖動無響應(yīng)。這就是單線程帶來的問題。
解決辦法:使用多線程,我們自己創(chuàng)建線程。把計(jì)算代碼放入我們自己寫的線程中,UI線程就能繼續(xù)做他的界面響應(yīng)了。
--------------------------------------------------------------------------------
線程的創(chuàng)建
--------------------------------------------------------------------------------
線程的實(shí)現(xiàn):線程一定是要執(zhí)行一段代碼的,所以要產(chǎn)生一個線程,必須先為該線程寫一個方法,這個方法中的代碼,就是該線程中要執(zhí)行的代碼,然而啟動線程時,是通過委托調(diào)用該方法的。線程啟動是,調(diào)用傳過來的委托,委托就會執(zhí)行相應(yīng)的方法,從而實(shí)現(xiàn)線程執(zhí)行方法。
//創(chuàng)建線程
private void button1_Click(object sender, EventArgs e)
{
//ThreadStart是一個無參無返回值的委托。
ThreadStart ts = new ThreadStart(js);
//初始化Thread的新實(shí)例,并通過構(gòu)造方法將委托ts做為參數(shù)賦初始值。
Thread td = new Thread(ts); //需要引入System.Threading命名空間
//運(yùn)行委托
td.Start();
}
//創(chuàng)建的線程要執(zhí)行的函數(shù)。
void js()
{
for (int i = 0; i < 1000000000; i++)
{
i += 1;
}
MessageBox.Show("提示出現(xiàn)前后窗體都能被拖動");
}
把這個計(jì)算寫入自己寫的線程中,就解決了單線程中的界面無反應(yīng)缺陷。
--------------------------------------------------------------------------------
小結(jié):創(chuàng)建線程的4個步驟:1.編寫線程索要執(zhí)行的方法。2.引用System.Threading命名空。3.實(shí)例化Thread類,并傳入一個指向線程所要運(yùn)行方法的委托。4.調(diào)用Start()方法,將該線程標(biāo)記為可以運(yùn)行的狀態(tài),但具體執(zhí)行時間由cpu決定。
--------------------------------------------------------------------------------
方法重入(多個線程執(zhí)行一個方法)
--------------------------------------------------------------------------------
由于線程可與同屬一個進(jìn)程的其它線程共享進(jìn)程所擁有的全部資源。
所以多個線程同時執(zhí)行一個方法的情況是存在的,然而這里不經(jīng)過處理的話會出現(xiàn)一點(diǎn)問題,線程之間先后爭搶資源,致使數(shù)據(jù)計(jì)算結(jié)果錯亂。
public partial class 方法重入 : Form
{
public 方法重入()
{
InitializeComponent();
//設(shè)置TextBox類的這個屬性是因?yàn)?,開啟ui線程,
//微軟設(shè)置檢測不允許其它線程對ui線程的數(shù)據(jù)進(jìn)行訪問,這里我們把檢測關(guān)閉,也就允許了其它線程對ui線程數(shù)據(jù)的訪問。
//如果檢測不設(shè)置為False,則報(bào)錯。
TextBox.CheckForIllegalCrossThreadCalls = false;
}
private void button1_Click(object sender, EventArgs e)
{
textBox1.Text = "0";
//開啟第一個線程,對js方法進(jìn)行計(jì)算
ThreadStart ts = new ThreadStart(js);
Thread td = new Thread(ts);
td.Start();
//開啟第二個線程,對js方法進(jìn)行計(jì)算
ThreadStart ts1 = new ThreadStart(js);
Thread td1 = new Thread(ts1);
td1.Start();
}
//多線程要重入的方法。
void js()
{
int a = Convert.ToInt32(textBox1.Text);
for (int i = 0; i < 2000; i++)
{
a++;
textBox1.Text = a.ToString();
}
}
}
出錯現(xiàn)象:點(diǎn)擊按鈕后TextBox1中數(shù)據(jù)為2000+或2000,如果你看到的數(shù)據(jù)一直是2000說明你的計(jì)算機(jī)cpu比較牛X,這樣的話你想看到不是2000的話,你可以多點(diǎn)擊幾次試試,真不行的話,代碼中給TextBox1賦值為0,換做在界面中給textBox1數(shù)值默認(rèn)值為0試試看。
出錯原因:兩個進(jìn)程同時計(jì)算這個方法,不相干擾應(yīng)該每個線程計(jì)算的結(jié)果都是2000的,但是這里的結(jié)果輸出卻讓人以外,原因是第一個兩個線程同時計(jì)算,并不是同時開始計(jì)算,而是根據(jù)cpu決定的哪個先開始,哪個后開始,雖然相差時間不多,但后開始的就會取用先開始計(jì)算過的數(shù)據(jù)計(jì)算,這樣就會導(dǎo)致計(jì)算錯亂。
解決辦法:解決這個的一個簡單辦法解釋給方法加鎖,加鎖的意思就是第一個線程取用過這個資源完畢后,第二個線程再來取用此資源。形成排隊(duì)效果。
下面給方法加鎖。
//多線程要重入的方法,這里加鎖。
void js()
{
lock (this)
{
int a = Convert.ToInt32(textBox1.Text);
for (int i = 0; i < 2000; i++)
{
a++;
textBox1.Text = a.ToString();
}
}
}
給方法加過鎖后,線程一前一后取用資源,就能避免不可預(yù)計(jì)的錯亂結(jié)果,第一個線程計(jì)算為2000,第二個線程計(jì)算就是從2000開始,這里的結(jié)果就為4000。
--------------------------------------------------------------------------------
小結(jié):多線程可以同時運(yùn)行,提高了cpu的效率,這里的同時并不是同時開始,同時結(jié)束,他們的開始是由cpu決定的,時間相差不大,但會有不可預(yù)計(jì)的計(jì)算錯亂,這里要注意類似上面例子導(dǎo)致的方法重入問題。
--------------------------------------------------------------------------------
前臺線程后臺線程
--------------------------------------------------------------------------------
.Net的公用語言運(yùn)行時能區(qū)分兩種不同類型的線程:前臺線程和后臺線程。這兩者的區(qū)別就是:應(yīng)用程序必須運(yùn)行完所有的前臺線程才可以退出;而對于后臺線程,應(yīng)用程序則可以不考慮其是否已經(jīng)運(yùn)行完畢而直接退出,所有的后臺線程在應(yīng)用程序退出時都會自動結(jié)束。
問題:關(guān)閉了窗口,消息框還能彈出。
private void button1_Click(object sender, EventArgs e)
{
//開啟一個線程,對js方法進(jìn)行計(jì)算
ThreadStart ts2 = new ThreadStart(js);
Thread td2 = new Thread(ts2);
td2.Start();
}
void js()
{
for (int i = 0; i < 2000000000; i++) //如果看不出效果這里的2后面多加0
{
i++;
}
MessageBox.Show("關(guān)閉了窗口我還是要出來的!");
}
原因:.Net環(huán)境使用Thread建立線程,線程默認(rèn)為前臺線程。即線程屬性IsBackground=false,而前臺線程只要有一個在運(yùn)行則應(yīng)用程序不關(guān)閉,所以知道彈出消息框后應(yīng)用程序才算關(guān)閉。
解決辦法:在代碼中設(shè)置td2.IsBackground=true;
--------------------------------------------------------------------------------
線程執(zhí)行帶參數(shù)的方法
--------------------------------------------------------------------------------
//創(chuàng)建一個執(zhí)行帶參數(shù)方法的線程
private void button1_Click(object sender, EventArgs e)
{
//ParameterizedThreadStart這是一個參數(shù)類型為object的委托
ParameterizedThreadStart pts=new ParameterizedThreadStart(SayHello);
Thread td2 = new Thread(pts);
td2.Start("張三"); //參數(shù)值先入這里
}
void SayHello(object name)
{
MessageBox.Show("你好,"+name.ToString()+"!");
}
線程執(zhí)行帶多參數(shù)的方法
--------------------------------------------------------------------------------
其實(shí)還是帶一參數(shù)的方法,只不過是利用參數(shù)類型為object的好處,這里將類型傳為list類型,貌似多參。
//創(chuàng)建一個執(zhí)行帶多個參數(shù)的方法線程
private void button1_Click(object sender, EventArgs e)
{
List<string> list = new List<string> { "張三", "李四", "王五" };
//ParameterizedThreadStart這是一個參數(shù)類型為object的委托
ParameterizedThreadStart pts=new ParameterizedThreadStart(SayHello);
Thread td2 = new Thread(pts);
td2.Start(list); //參數(shù)值先入這里
}
void SayHello(object list)
{
List<string> lt = list as List<string>;
for (int i = 0; i < lt.Count; i++)
{
MessageBox.Show("你好," + lt[i].ToString() + "!");
}
}
總結(jié):看到這里相信對多線程應(yīng)該有一個初步的了解了,我就不說了。
相關(guān)文章
C#?wpf?無邊框窗口添加陰影效果的實(shí)現(xiàn)
在本篇內(nèi)容中小編給大家整理了一篇關(guān)于C#?wpf?無邊框窗口添加陰影效果的具體方法內(nèi)容,有興趣的朋友們可以學(xué)習(xí)參考下2022-11-11DataTables List互相轉(zhuǎn)換的實(shí)現(xiàn)類示例
這篇文章主要介紹了將DataTable轉(zhuǎn)換為List,將List轉(zhuǎn)換為DataTable的實(shí)現(xiàn)類實(shí)例方法,大家參考使用吧2013-11-11- RSA解決了對稱加密的一個不足,比如AES算法加密和解密時使用的是同一個秘鑰,因此這個秘鑰不能公開,因此對于需要公開秘鑰的場合,我們需要在加密和解密過程中使用不同的秘鑰,加密使用的公鑰可以公開,解密使用的私鑰要保密,這就是非對稱加密的好處?!?/div> 2021-06-06
C# 表達(dá)式目錄樹Expression的實(shí)現(xiàn)
本文主要介紹了C# 表達(dá)式目錄樹Expression的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-09-09C#結(jié)合SMTP實(shí)現(xiàn)郵件報(bào)警通知的實(shí)現(xiàn)示例
這篇文章主要介紹了C#結(jié)合SMTP實(shí)現(xiàn)郵件報(bào)警通知的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07C#中GridView動態(tài)添加列的實(shí)現(xiàn)方法
這篇文章主要介紹了C#中GridView動態(tài)添加列的實(shí)現(xiàn)方法,涉及C#中GridView的相關(guān)使用技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-07-07C# 設(shè)計(jì)模式之單例模式歸納總結(jié)
這篇文章主要介紹了C#設(shè)計(jì)模式之單例模式實(shí)例講解,本文講解了單例模式的定義、單例模式的優(yōu)缺點(diǎn),需要的朋友可以參考下2017-04-04最新評論