久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

C#多線程之旅(2):創(chuàng)建和開始線程

 weijianian 2016-08-07


來源:jackson0714

鏈接:http://www.cnblogs.com/jackson0714/p/5103898.html 


一,、線程的創(chuàng)建和開始


在第一篇的介紹中,,線程使用Thread 類的構(gòu)造函數(shù)來創(chuàng)建,通過傳給一個ThreadStart 委托來實(shí)現(xiàn)線程在哪里開始執(zhí)行,。下面是ThreadStart的定義:


// Summary:

//     Represents the method that executes on a System.Threading.Thread.

[ComVisible(true)]

public delegate void ThreadStart();


調(diào)用一個Start方法,,然后設(shè)置它開始運(yùn)行。線程會一直運(yùn)行直到這個方法返回,然后這個線程結(jié)束,。


下面是一個例子,,使用擴(kuò)展C#語法創(chuàng)建一個ThreadStart委托:2.1_ThreadStart


class ThreadTest

{

    static void Main()

    {

        Thread t = new Thread(new ThreadStart(Go));

        t.Start();

        Go();

        Console.ReadKey();

    }

    static void Go()

    {

        Console.WriteLine('hello!');

    }

}


在這個例子中,thread t執(zhí)行Go(),基本上與主線同時(shí)程調(diào)用Go()方法,,結(jié)果是打印出兩個時(shí)間接近的hello,。


一個線程可以被方便的創(chuàng)建通過指定一個方法組,然后由C#推斷出ThreadStart委托:


2.2_Thread


class Program

{

    static void Main(string[] args)

    {

        Thread t = new Thread(Go);

        t.Start();

        Go();

        Console.ReadKey();

    }

 

    static void Go()

    {

        Console.WriteLine('Go');

    }

}


另外一種更簡單的方式是使用lambda表達(dá)式或者匿名方法:2.3_LambaExpression


static void Main(string[] args)

{

    Thread t = new Thread(()=>Console.WriteLine('Go'));

    t.Start();

    Console.ReadKey();

}


二,、傳遞數(shù)據(jù)給一個線程


1.利用Lambda傳遞一個數(shù)據(jù)


傳遞參數(shù)給線程的目標(biāo)方法的最簡單的方法是執(zhí)行一個lambda表達(dá)式,,該表達(dá)式調(diào)用一個方法并傳遞期望的參數(shù)給這個方法。


2.4_PassingDataToAThread


static void Main(string[] args)

{

    Thread t = new Thread(() => Print('A'));

    t.Start();

    Console.ReadKey();

}

 

static void Print(string message)

{

    Console.WriteLine(message);

}


2.傳遞多個參數(shù)


通過這種方式,,你可以傳遞任意數(shù)量的參數(shù)給這個方法,。你甚至可以將整個實(shí)現(xiàn)包裝在一個多語句的lambda中:


2.5_PassingDataToAThread


new Thread(() =>

{

    Console.WriteLine('a');

    Console.WriteLine('b');

}).Start();


你也可以簡單的在C# 2.0里面那樣使用匿名方法做同樣的事:


new Thread(delegate()

{

    Console.WriteLine('a');

    Console.WriteLine('b');

}).Start();


3.利用Thread.Start傳遞參數(shù)


另外一種方式是傳遞一個參數(shù)給ThreadStart方法:


2.6_PassingDataToAThread_ThreadStart


static void Main(string[] args)

{

    Thread t = new Thread(Print);

    t.Start('A');

    Console.ReadKey();

}

static void Print(object messageObj)

{

    string message = (string)messageObj;//必須進(jìn)行轉(zhuǎn)換

    Console.WriteLine(message);

}


這種方式能夠工作是因?yàn)?/span>Thread的構(gòu)造函數(shù)是重載的,,接受下面兩種中的任意一種委托:


// Summary:

//     Represents the method that executes on a System.Threading.Thread.

[ComVisible(true)]

public delegate void ThreadStart();

 

// Summary:

//     Represents the method that executes on a System.Threading.Thread.

//

// Parameters:

//   obj:

//     An object that contains data for the thread procedure.

[ComVisible(false)]

public delegate void ParameterizedThreadStart(object obj);


這個ParameterizedThreadStart的只允許接收一個參數(shù),。而且因?yàn)樗念愋褪?strong>object,所以通常需要轉(zhuǎn)換,。


4.Lambda表達(dá)式和捕獲變量


由我們上面看到的例子可以知道,,一個lambda式在傳遞數(shù)據(jù)給線程是最用的。然而,,你必須非常小心在開始線程后意外修改捕獲變量,,因?yàn)檫@些變量是共享的。比如下面的:


2.7_LbdaExpressionsAndCapturedVariables


for(int i =0;i10;i++)

{

    new Thread(() => Console.Write(i)).Start();

}


這個輸出是不確定的,,下面是一種典型的情況:



這里的問題是變量ifor循環(huán)執(zhí)行時(shí)指向同一個內(nèi)存地址,。因此,每一個線程調(diào)用Console.Write時(shí),,i的值有可能在這個線程運(yùn)行時(shí)改變,。


解決方案是使用一個臨時(shí)變量:


2.8_LambdaExpressionsAndCapturedVariables_Solution


for (int i = 0; i 10; i++)

{

    int temp = i;

    new Thread(() => Console.Write(temp)).Start();

}


變量temp在每個循環(huán)迭代中位于不同的內(nèi)存塊。因此每一個線程捕獲到了不同的內(nèi)存位置,,而且沒有問題,。我們可以解釋在之前的代碼中的問題:


2.9_PassingData_TemporaryVariable


string text = 'A';

Thread a = new Thread(() => Console.WriteLine(text));

 

text = 'B';

Thread b = new Thread(() => Console.WriteLine(text));

 

a.Start();

b.Start();


因?yàn)閮蓚€lambda表達(dá)式捕獲同樣的text的值,所以B被打印出兩次,。



三,、命名線程


每一個線程有一個Name屬性你可以方便用來debugging.當(dāng)線程顯示在Visual Statudio里面的Threads WindowDebug Loaction toolbar的時(shí)候,線程的Name屬性是特別有用的,。你可以只設(shè)置線程的名字一次,;之后嘗試改變它將會拋出異常信息。


靜態(tài)的Thread.CurrentThread屬性代表當(dāng)前執(zhí)行的線程,。


在下面的例子2.10_NamingThread中,,我們設(shè)置了主線程的名字:


static void Main(string[] args)

{

    Thread.CurrentThread.Name = 'Main Thread';

    Thread t = new Thread(Go);

    t.Name = 'Worker Thread';

    t.Start();

    Go();

    Console.ReadKey();

}

static void Go()

{

    Console.WriteLine('Go! The current thread is {0}', Thread.CurrentThread.Name);

}


四、前臺線程和后臺線程


默認(rèn)情況下,你自己顯示創(chuàng)建的線程是前臺線程,。前臺線程保持這個應(yīng)用程序一直存活只要其中任意一個正在運(yùn)行,,而后臺線程不是這樣的。一旦所有的前臺線程完成,,這個應(yīng)用程序就結(jié)束了,, 任何正在運(yùn)行的后臺線程立刻終止。


一個線程前臺/后臺的狀態(tài)跟它的優(yōu)先級和配置的執(zhí)行時(shí)間沒有關(guān)聯(lián),。


你可以使用線程的IsBackgroud屬性查詢或改變一個線程的后臺狀態(tài),。


下面是例子:2.11_PriorityTest


static void Main(string[] args)

{

    Thread t = new Thread(() => Console.ReadKey());

    if (args.Length > 0)//如果Main方法沒有傳入?yún)?shù)

    {

        //設(shè)置線程為后臺線程,等待用戶輸入,。

        //因?yàn)橹骶€程在t.Start()執(zhí)行之后就會終止,,

        //所以后臺線程t會在主線程退出之后,立即終止,,應(yīng)用程序就會結(jié)束,。

        t.IsBackground = true;

    }

    t.Start();

}


如果程序調(diào)用的時(shí)候傳入了參數(shù),則創(chuàng)建的線程為前臺線程,,然后等待用戶輸入,。同時(shí),如果主線程退出,,應(yīng)用程序?qū)⒉粫顺?,因?yàn)榍芭_線程t沒有退出。另一方面,,如果main方法傳入了參數(shù),,則創(chuàng)建的線程設(shè)置為后臺線程。當(dāng)主線程退出時(shí),,應(yīng)用程序立即退出,。


當(dāng)一個進(jìn)程以這種方式終止,則任何后臺線程執(zhí)行棧里面的finally 語句塊將會被規(guī)避,。如果你的線程使用finally(or using)語句塊去執(zhí)行如釋放資源或者刪除臨時(shí)文件的清理工作,,這將是一個問題。為了避免這個,,你可以顯示地等待后臺線程退出應(yīng)用程序,。


這里有兩種實(shí)現(xiàn)方式:


  1. 如果你自己創(chuàng)建了這個線程,可以在這個線程上調(diào)用Join方法,。


  2. 如果你使用線程池,,可以使用一個事件去等待處理這個線程。


在這兩種情況下,,你需要指定一個timeout,,因此可以結(jié)束一個由于某些原因拒絕完成的線程,。這是你的備選退出策略:在最后,你想要你的應(yīng)用程序關(guān)閉,,不需要用戶從任務(wù)管理器中刪除,。


如果用戶使用任務(wù)管理器強(qiáng)制結(jié)束一個.NET進(jìn)程,所有的線程像是后臺線程一樣終止,。這個是觀察到的行為,,所以會因?yàn)?span lang='en-US'>CLR和操作系統(tǒng)的版本而不同。


前臺線程不需要這樣對待,,但是你必須小心避免可能造成線程不能結(jié)束的bugs,。造成應(yīng)用程序不能正確地退出的一個通常的原因是有激活的前臺線程還存活在。


五,、線程優(yōu)先級


一個線程的優(yōu)先級決定了在操作系統(tǒng)中它可以得到多少相對其他線程的執(zhí)行時(shí)間,,下面是線程優(yōu)先級的等級:


// Summary:

//     Specifies the scheduling priority of a System.Threading.Thread.

[Serializable]

[ComVisible(true)]

public enum ThreadPriority

{

    Lowest = 0,

    BelowNormal = 1,

    Normal = 2,

    AboveNormal = 3,

    Highest = 4,

}


當(dāng)多線程同時(shí)是激活的,線程優(yōu)先級是很重要的,。


注意:提高線程優(yōu)先級時(shí),,需要非常小心,這將可能導(dǎo)致其他線程對資源訪問的饑餓狀態(tài)的問題,。


當(dāng)提升一個線程的優(yōu)先級時(shí),,不會使它執(zhí)行實(shí)時(shí)工作,,因?yàn)樗粦?yīng)用程序的進(jìn)程優(yōu)先級限制了,。為了執(zhí)行實(shí)時(shí)工作,你也必須通過使用System.DiagnosticesProcess類來提升進(jìn)程的優(yōu)先級:


using (Process p = Process.GetCurrentProcess())

{

    p.PriorityClass = ProcessPriorityClass.High;

}


ProcessPriorityClass.High事實(shí)上是優(yōu)先級最高的一檔:實(shí)時(shí),。設(shè)置一個進(jìn)程優(yōu)先級到實(shí)時(shí)狀態(tài)將會導(dǎo)致其他線程無法獲得CPU時(shí)間片,。如果你的應(yīng)用程序意外地進(jìn)入一個無限循環(huán)的狀態(tài),你甚至?xí)l(fā)現(xiàn)操作被鎖住了,,只有電源鍵能夠拯救你了,。針對這個原因,High通常對于實(shí)時(shí)應(yīng)用程序是最好的選擇,。


如果你的實(shí)時(shí)應(yīng)用程序有一個用戶界面,,提高程序的優(yōu)先級將會使刷新界面占用昂貴的CPU的時(shí)間,且會使整個系統(tǒng)變得運(yùn)行緩慢(尤其是UI很復(fù)雜的時(shí)候),。降低主線程優(yōu)先級且提升進(jìn)程的優(yōu)先級來確保實(shí)時(shí)線程不會被界面重繪所搶占,,但是不會解決其他進(jìn)程對CPU訪問缺乏的問題,因?yàn)椴僮飨到y(tǒng)整體上會一直分配不成比例的資源給進(jìn)程,。


一個理想的解決方案是讓實(shí)時(shí)線程和用戶界面用不同的優(yōu)先級運(yùn)行在不同的進(jìn)程中,,通過遠(yuǎn)程和內(nèi)存映射文件來通信。即使提高了進(jìn)程優(yōu)先級,,在托管環(huán)境中處理硬實(shí)時(shí)系統(tǒng)需求還是對適用性有限制,。此外,,潛藏的問題會被自動垃圾回收引進(jìn),操作系統(tǒng)會遇到新的挑戰(zhàn),,即使是非托管代碼,,使用專用硬件或者特殊的實(shí)時(shí)平臺,那將被最好的解決,。


六,、異常處理


在任何try/catch/finally 語句塊作用域內(nèi)創(chuàng)建的線程,當(dāng)這個線程開始時(shí),,這個線程和語句塊是沒有關(guān)聯(lián)的,。


思考下面的程序:


參考例子:2.12_ExceptionHandling


static void Main(string[] args)

{

    try

    {

        new Thread(Go).Start();

    }

    catch(Exception ex)

    {

        Console.WriteLine('Exception');

    }

    Console.ReadKey();

}

static void Go()

{

    throw null;

}


try/catch 聲明在這個例子中是無效的,而且新創(chuàng)建的線程將會被一個未處理的NullReferenceException所阻斷,。當(dāng)你考慮每一個線程有一個單獨(dú)的執(zhí)行路徑這種行為是說得通的,。


改進(jìn)方法是將exception handler移到Go()的方法中:


參考例子:2.13_ExceptionHandling_Remedy


class Program

{

    static void Main(string[] args)

    {

        new Thread(Go).Start();

        Console.ReadKey();

    }

 

    static void Go()

    {

        try

        {

            throw null;

        }

        catch (Exception ex)

        {

            Console.WriteLine(ex.Message);

        }

    }

}


你需要在應(yīng)用程序中的所有線程入口方法中添加一個exception handler ,就像你在主線程中做的那樣,。一個未處理的線程會造成整個應(yīng)用程序關(guān)閉,,而且會彈出一個不好看的窗口。


在寫這個exception handling 語句塊時(shí),,你可能極少忽略這個問題,,典型情況是,你可能會記錄exception的詳細(xì)信息,,然后可能顯示一個窗口讓用戶去自動去提交這些信息到你的web server上,。然后你可能會關(guān)掉這個應(yīng)用程序因?yàn)檫@個error毀壞了程序的狀態(tài)。然后,,這樣做的開銷是用戶可能會丟失他最近的工作,,比如打開的文檔。


對于WPFWinForm應(yīng)用程序來說,,全局的exception handling 事件(Application.DispatcherUnhandlerException Application.ThreadException)只會檢測到主UI線程上的拋出的異常,。你還是必須手動處理線程的異常。


AppDomain.CurrentDomain.UnhandledException可以檢測任何未處理的異常,,但是無法阻止應(yīng)用程序之后關(guān)閉,。


然而,某些情形下你不需要在線程上處理異常,,因?yàn)?span lang='en-US'>.NET Framework為你做了這個,。下面是沒有提及的內(nèi)容:


Asynchronous delegates


BackgroudWorker


The Task Parallel Library(conditions apply)


    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點(diǎn),。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多