<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      C# 溫故而知新: 線程篇(一)

      c# 溫故而知新: 線程篇(一)

      Thread

      目錄:

       

       

       

      1 線程基礎的簡單介紹

       

       

      首先讓我們翻開書本來了解下線程的一些基礎知識:

      1 線程有時被稱為輕量級進程,程序執行流的最小單元

      2 線程時由線程ID,當前指令指針(PC),寄存器集合堆棧組成。

      3 線程自身不能擁有系統資源,但是可以使用線程所屬進程所占有的系統資源

      4 線程可以創建和撤銷另一個線程

      5 線程可以擁有自身的狀態,例如 運行狀態,掛起狀態,銷毀釋放狀態等等

      6 線程具有優先級,每個線程都分配了0-31 級別的其中一個優先級,數字越大,優先級越高,然而手動分配優先級過于復雜,

      所以微軟為我們的Thread類提供一個優先級的枚舉,ThreadPriority枚舉便是優先級枚舉,我們可以利用thread.Priority屬性來進行設置

      7 線程開銷,這個是個復雜的話題,希望有機會的話能夠單獨寫一遍文章解釋下

       

       

       

       

       

       

       

       

         

       

      那么多線程有什么實際好處呢?

      首先讓我們了解下多線程的概念:個程序或者進程中同時運行多個線程完成不同的工作

      從概念中我們便可知道多線程的優點了

      1 能夠實現并行操作,也就是說多個線程可以同時進行工作

      2 利用多線程后許多復雜的業務或者是計算可以交給后臺線程去完成,從而提高整體程序的性能

      3 類似于第一條利用多線程可以達到異步的作用(注意,實現異步的一種方式是多線程

       

       

       

       

       

      當然多線程也有一定的問題需要注意,那就是線程同步問題,關于這個問題我會今后的文章中詳細說明

       

      2 線程同步與線程異步的簡單介紹

      *1 線程同步

      關于線程同步的概念最簡單的理解就是

      同步方法調用在程序繼續執行之前,需要等待同步方法執行完畢返回結果

      很有可能多個線程都會對一個資源進行訪問,從而導致資源被破壞,所以必須采用線程的同步機制,例如為共享資源加鎖

      ,當其中一個線程占有了鎖之后,其余線程均不能使用共享資源,只有等其釋放鎖之后,接下來的其中一個線程會占有該

      鎖,本系列會從Thread類開始講起,以后多章都會討論線程同步機制,例如鎖機制,臨界區,互斥,信號量 同步事件等待句柄; 等等

       

       

       

       

       

      *2 線程異步

      線程異步指的是一個調用請求發送給被調用者,而調用者不用等待其結果的返回,一般異步執行的任務都需要比較長的時間,

      所以為了不影響主線程的工作,可以使用多線程或者新開辟一個線程來實現異步,同樣,異步和線程池也有著非常緊密的聯系,

      這點我會在今后有關線程池的文章中詳細敘述,線程池和異步線程將在第二章中詳細闡述下

       

       

      3 前臺線程與后臺線程的簡單介紹

      前臺線程:

      諸如我們Console程序的主線程,wpf或者sliverlight的 界面線程等等,都屬于前臺線程,一旦前臺線程奔潰或者終止,相應的后臺

      線程都會終止本章中通過Thread類產生的線程默認都是前臺線程,當然我們可以設置Thread的屬性讓該對象成為后臺線程,必須

      注意的是,一旦前臺線程全部運行完畢,應用程序的進程也會釋放,但是假設Console程序中main函數運行完畢,但是其中幾個前臺

      線程還處在運行之中,那么這個Console程序的進程是不會釋放的,仍然處于運行之中,直到所有的前臺線程都釋放為止      

       

      后臺線程:

      和前臺線程唯一的區別是,后臺線程更加默默無聞,甚至后臺線程因某種情況,釋放銷毀時不會影響到進程,也就是說后臺線程釋放時

      不會導致進程的釋放

      用一個例子再來說明下前后臺線程的區別:

      有時我們打開outlook 后接受郵件時,程序會失去響應或被卡住,這時候我們去點擊outlook時系統會提示 outlook 失去響應,是否等待或者關閉,

      當我們點擊關閉時,其實在程序中關于outlook的所有運行的前臺線程被終止,導致了outlook被關閉了其進程也隨之釋放消失。但是,當我們在

      outlook中點擊更新郵件時,后臺線程會去收取郵件的工作,我們可以在此期間關閉 outlook接受新郵件的后臺線程,而不會導致整個outlook的關閉

       

       

       

       

       

       

      4 細說下Thread 最為關鍵的構造函數

      相信大家再看過前幾章對于線程的介紹后,對線程應該有一個溫故的感覺,那么讓我們開始對thread這個線程類進行深層次的研究下,

      首先要啟動一個線程必須將該線程將要做的任務告訴該線程,否則,線程會不知道干什么事導致線程無意義的開啟,浪費系統資源,果然,

      Thread類的構造函數提供了以下的版本

      ThreadStart 和 ParameterThreadStart 參數都是委托,所以可以看出委托其實就是方法的抽象,前者用于不帶參數的并且無返回值的

      方法的抽象后者是帶object參數的方法的抽象,大家通過以下簡單的方法注意下線程如何調用帶參數的方法

       public class ThreadStartTest 
          {
              //無參數的構造函數
              Thread thread = new Thread(new ThreadStart(ThreadMethod));
              //帶有object參數的構造函數
              Thread thread2 = new Thread(new ParameterizedThreadStart(ThreadMethodWithPara));
              public ThreadStartTest() 
              {
                  //啟動線程1
                  thread.Start();
                  //啟動線程2
                  thread2.Start(new Parameter { paraName="Test" });
              }
              static void ThreadMethod() 
              {
                 //....
              }
              static void ThreadMethodWithPara(object o) 
              {
                  if (o is Parameter) 
                  {
                     // (o as Parameter).paraName.............
                  }
              }
      
      
          }
      
          public class Parameter 
          {
              public string paraName { get; set; }
          }

      不帶參數的方法似乎很簡單的能被調用,只要通過第一個構造函數便行,對于帶參數的方法,大家注意下參數是如何傳入線程所調用的方法,

      當啟動線程時,參數通過thread.Start方法傳入,于是我們便成功啟動了thread線程,大伙可千萬不要小看基礎啊,往往在復雜的項目中很多

      就是因為一些基礎導致,所以一定不要忽視它。。。

       

      5 細說下Thread 的 Sleep方法

        話說微軟對Thread.Sleep方法的解釋過于簡單,導致許多人會誤認為這個方法并不重要,其實這是錯誤的,其實線程是非常復雜的,

        而且我們圍繞這個方法來溫故下windows系統對于CPU競爭的策略:

        所謂搶占式操作系統,就是說如果一個進程得到了 CPU 時間,除非它自己放棄使用 CPU ,否則將完全霸占 CPU 。因此可以看出,

        在搶占式操作系統中,操作系統假設所有的進程都是“人品很好”的,會主動退出 CPU 。

        發現寫到這里貌似真的已經比較復雜了,由于本人對操作系統底層的知識比較匱乏,決定還是引用下別人的理解,順便自己也學習下

      引用:

      假設有源源不斷的蛋糕(源源不斷的時間),一副刀叉(一個CPU),10個等待吃蛋糕的人(10 個進程)。如果是 Unix 操作系統來負責分蛋糕,

      那么他會這樣定規矩:每個人上來吃 1 分鐘,時間到了換下一個。最后一個人吃完了就再從頭開始。于是,不管這10個人是不是優先級不同、饑餓

      程度不同、飯量不同,每個人上來的時候都可以吃 1 分鐘。當然,如果有人本來不太餓,或者飯量小,吃了30秒鐘之后就吃飽了,那么他可以跟操

      作系統說:我已經吃飽了(掛起)。于是操作系統就會讓下一個人接 著來。如果是 Windows 操作系統來負責分蛋糕的,那么場面就很有意思了。

      他會這樣定規矩:我會根據你們的優先級、饑餓程度去給你們每個人計算一個優先級。優先級最高的那個人,可 以上來吃蛋糕——吃到你不想吃為止。

      等這個人吃完了,我再重新根據優先級、饑餓程度來計算每個人的優先級,然后再分給優先級最高的那個人。這樣看來,這個 場面就有意思了——

      可能有些人是PPMM,因此具有高優先級,于是她就可以經常來吃蛋糕。可能另外一個人的優先級特別低,于是好半天了才輪到他一次(因為 隨著時間

      的推移,他會越來越饑餓,因此算出來的總優先級就會越來越高,因此總有一天會輪到他的)。而且,如果一不小心讓一個大胖子得到了刀叉,因為他

      飯量 大,可能他會霸占著蛋糕連續吃很久很久,導致旁邊的人在那里咽口水。。。而且,還可能會有這種情況出現:操作系統現在計算出來的結果,是

      5號PPMM總優 先級最高——高出別人一大截。因此就叫5號來吃蛋糕。5號吃了一小會兒,覺得沒那么餓了,于是說“我不吃了”(掛起)。因此操作系

      統就會重新計算所有人的 優先級。因為5號剛剛吃過,因此她的饑餓程度變小了,于是總優先級變小了;而其他人因為多等了一會兒,饑餓程度都變大了,

      所以總優先級也變大了。不過這時 候仍然有可能5號的優先級比別的都高,只不過現在只比其他的高一點點——但她仍然是總優先級最高的啊。因此操作

      系統就會說:5號mm上來吃蛋糕……(5號 mm心里郁悶,這不剛吃過嘛……人家要減肥……誰叫你長那么漂亮,獲得了那么高的優先級)。那么,

      Thread.Sleep 函數是干嗎的呢?還用剛才的分蛋糕的場景來描述。上面的場景里面,5號MM在吃了一次蛋糕之后,覺得已經有8分飽了,她覺得在未來

      的半個小時之內都不想再 來吃蛋糕了,那么她就會跟操作系統說:在未來的半個小時之內不要再叫我上來吃蛋糕了這樣,操作系統在隨后的半個小時

      里面重新計算所有人總優先級的時候, 就會忽略5號mm。Sleep函數就是干這事的,他告訴操作系統“在未來的多少毫秒內我不參與CPU競爭”。

       

       

       

       

       

       

       

       

       

       

       

       

       

       

       

       

       

       

      6 細說下Thread 的 join 方法

      為什么我要把Thread.Join()方法單獨細說下,個人認為join方法非常重要,

      在細說前我想再次強調下主線程和子線程的區別:

       

      首先大家肯定知道在Console程序中,主線程自上而下著運行著main函數,假如我們在main函數中新增一個線程thread對象的話,

      也就是說,在主線程中再開啟一個子線程,同時子線程和主線程可以同時工作(前提是子線程使用Start方法),同理,假如我在這

      個子線程中再開辟一個屬于這個子線程的子線程,同理這3個爺爺,父親,兒子線程也可以使用Start()方法一起工作,假如在主線

      程中添加2個thread對象并開啟,那么這2 線程便屬于同一層次的線程(兄弟線程)(和優先級無關,只同一位置層次上的兄弟),

      有可能上述的讓你覺得郁悶或者難以理解?沒關系看簡單例子就能夠理解了

       

           public static void ShowFatherAndSonThread(Thread grandFatherThread)
              {
                  Console.WriteLine("爺爺主線程名:{0}", grandFatherThread.Name);
                  Thread brotherThread = new Thread(new ThreadStart(() => { Console.WriteLine("兄弟線程名:{0}", Thread.CurrentThread.Name); }));
                  Thread fatherThread = new Thread(new ThreadStart(
                      () =>
                      {
                          Console.WriteLine("父親線程名:{0}", Thread.CurrentThread.Name);
                          Thread sonThread = new Thread(new ThreadStart(() =>
                          {
                              Console.WriteLine("兒子線程名:{0}", Thread.CurrentThread.Name);
                          }));
                          sonThread.Name = "SonThread";
                          sonThread.Start();
                      }
                          ));
                  fatherThread.Name = "FatherThread";
                  brotherThread.Name="BrotherThread";
                  fatherThread.Start();
                  brotherThread.Start();
              }

      言歸正傳讓我們溫故下Jion方法,先看msdn中是怎么解釋的:

      繼續執行標準的 COM 和 SendMessage 消息泵處理期間,阻塞調用線程,直到某個線程終止為止。

      大家把注意力移到后面紅色的部分,什么是“調用線程”呢?如果你理解上述線程關系的話,可能已經理解了,主線程(爺爺輩)的調用了父親線程,

      父親線程調用了兒子線程,假設現在我們有一個奇怪的需求,必須開啟爺爺輩和父親輩的線程但是,爺爺輩線程必須等待父親線程結束后再進行,

      這該怎么辦? 這時候Join方法上場了,我們的目標是阻塞爺爺線程,那么后面的工作就明確了,讓父親線程(thread)對象去調用join方法就行

      一下是個很簡單的例子,讓大家再深入理解下。

              public static void ThreadJoin()
              {
                  Console.WriteLine("我是爺爺輩線程,子線程馬上要來工作了我得準備下讓個位給他。");
                  Thread t1 = new Thread(
                      new ThreadStart
                          (
                           () =>
                           {
                               for (int i = 0; i < 10; i++)
                               {
                                   if (i == 0)
                                       Console.WriteLine("我是父親線層{0}, 完成計數任務后我會把工作權交換給主線程", Thread.CurrentThread.Name);
                                   else
                                   {
                                       Console.WriteLine("我是父親線層{0}, 計數值:{1}", Thread.CurrentThread.Name, i);
                                   }
                                   Thread.Sleep(1000);
                               }
                           }
                          )
                      );
                  t1.Name = "線程1";
                  t1.Start();
                  //調用join后調用線程被阻塞
      t1.Join(); Console.WriteLine(
      "終于輪到爺爺輩主線程干活了"); }

      代碼中當父親線程啟動后會立即進入Jion方法,這時候調用該線程爺爺輩線程被阻塞,直到父親線程中的方法執行完畢為止,最后父親線程將控制

      權再次還給爺爺輩線程,輸出最后的語句。聰明的你肯定會問:兄弟線程怎么保證先后順序呢?很明顯如果不使用join,一并開啟兄弟線程后結果

      是隨機的不可預測的(暫時不考慮線程優先級),但是我們不能在兄弟線程全都開啟后使用join,這樣阻塞了父親線程,而對兄弟線程是無效的,

      其實我們可以變通一下,看以下一個很簡單的例子:

              public static void ThreadJoin2()
              {
                  IList<Thread> threads = new List<Thread>();
                  for (int i = 0; i < 3; i++)
                  {
                      Thread t = new Thread(
                          new ThreadStart(
                              () =>
                              {
      
                                  for (int j = 0; j < 10; j++)
                                  {
                                      if (j == 0)
                                          Console.WriteLine("我是線層{0}, 完成計數任務后我會把工作權交換給其他線程", Thread.CurrentThread.Name);
                                      else
                                      {
                                          Console.WriteLine("我是線層{0}, 計數值:{1}", Thread.CurrentThread.Name, j);
                                      }
      
                                      Thread.Sleep(1000);
                                  }
                              }));
                      t.Name = "線程" + i;
                      //將線程加入集合
                      threads.Add(t);
                  }
      
                  foreach (var thread in threads)
                  {
                      thread.Start();
                      //每次按次序阻塞調用次方法的線程
                      thread.Join();
                  }
              }

       輸出結果:

      但是這樣我們即便能達到這種效果,也會發現其中存在著不少缺陷:

      1:必須要指定順序

      2:一旦一個運行了很久,后續的線程會一直等待很久

      3: 很容易產生死鎖

      從前面2個例子能夠看出 jion是利用阻塞調用線程的方式進行工作,我們可以根據需求的需要而靈活改變線程的運行順序,但是在復雜的項目或業務中

      對于jion方法的調試和糾錯也是比較困難的。

       

      7 細說下Thread 的 Abort和 Interrupt方法

      Abort 方法:

      其實 Abort 方法并沒有像字面上的那么簡單,釋放并終止調用線程,其實當一個線程調用 Abort方法時,會在調用此方法的線程上引發一個異常:

      ThreadAbortException ,讓我們一步步深入下對這個方法的理解:

            1 首先我們嘗試對主線程終止釋放

        static void Main(string[] args)
              {
                  try
                  {
                      Thread.CurrentThread.Abort();
                  }
                  catch
                  {
                      //Thread.ResetAbort();
                      Console.WriteLine("主線程接受到被釋放銷毀的信號");
                      Console.WriteLine( "主線程的狀態:{0}",Thread.CurrentThread.ThreadState);
                  }
                  finally
                  {
                      Console.WriteLine("主線程最終被被釋放銷毀");
                      Console.WriteLine("主線程的狀態:{0}", Thread.CurrentThread.ThreadState);
                      Console.ReadKey();
                  }
      }

      從運行結果上看很容易看出當主線程被終止時其實報出了一個ThreadAbortException, 從中我們可以進行捕獲,但是注意的是,主線程直到finally語

      句塊執行完畢之后才真正結束(可以仔細看下主線程的狀態一直處于AbortRequest),如果你在finally語句塊中執行很復雜的邏輯或者計算的話,那

      么只有等待直到運行完畢才真正銷毀主線程(也就是說主線程的狀態會變成Aborted,但是由于是主線程所以無法看出).

       

      2 嘗試終止一個子線程

      同樣先看下代碼:

      static void TestAbort() 
              {
                  try
                  {
                      Thread.Sleep(10000);
                  }
                  catch 
                  {
                      Console.WriteLine("線程{0}接受到被釋放銷毀的信號",Thread.CurrentThread.Name);
                      Console.WriteLine("捕獲到異常時線程{0}主線程的狀態:{1}", Thread.CurrentThread.Name,Thread.CurrentThread.ThreadState);
                  }
                  finally
                  {
                      Console.WriteLine("進入finally語句塊后線程{0}主線程的狀態:{1}", Thread.CurrentThread.Name, Thread.CurrentThread.ThreadState);
                  }
              }
      
      Main:
      static void Main(string[] args)
              {
               
                  Thread thread1 = new Thread(TestAbort);
                  thread1.Name = "Thread1";
                  thread1.Start();
                  Thread.Sleep(1000);
                  thread1.Abort();
                  thread1.Join();
                  Console.WriteLine("finally語句塊后,線程{0}主線程的狀態:{1}", thread1.Name, thread1.ThreadState);
                  Console.ReadKey();
              }

       

      了解了主線程的銷毀釋放后,再來看下子線程的銷毀釋放的過程(Start->abortRequested->Aborted->Stop),從最后輸出的狀態變化來看,

      子線程thread1 的狀態變化是十分清楚的,幾乎和主線程的例子一致,唯一的區別是我們在 main方法中故意讓主線程阻塞這樣能看見thread 1

      在 finally語句塊后的狀態

      3,嘗試對尚未啟動的線程調用Abort

      如果對一個尚未啟動的線程調用Abort的話,一旦該線程啟動就被停止了

      4  嘗試對一個掛起的線程調用Abort

      如果在已掛起的線程上調用 Abort則將在調用 Abort 的線程中引發 ThreadStateException,并將 AbortRequested 添加到被中止的線程的

      ThreadState 屬性中。直到調用 Resume 后才在掛起的線程中引發 ThreadAbortException如果在正在執行非托管代碼的托管線程上調用 Abort

      則直到線程返回到托管代碼才引發 ThreadAbortException

       

       Interrupt 方法:

      Interrupt 方法將當前的調用該方法的線程處于掛起狀態,同樣在調用此方法的線程上引發一個異常:ThreadInterruptedException和Abort方法不同的是,

      被掛起的線程可以喚醒

              static void Main(string[] args)
              {
                  Thread thread1 = new Thread(TestInterrupt);
                  thread1.Name = "Thread1";
                  thread1.Start();
                  Thread.Sleep(1000);
                  thread1.Interrupt();
                  thread1.Join();
                  Console.WriteLine("finally語句塊后,線程{0}主線程的狀態:{1}", thread1.Name, thread1.ThreadState);
                  Console.ReadKey();
              }
              static void TestInterrupt() 
              {
                  try
                  {
                      Thread.Sleep(3000);
                  }
                  catch (ThreadInterruptedException e)
                  {
                      Console.WriteLine("線程{0}接受到被Interrupt的信號", Thread.CurrentThread.Name);
                      Console.WriteLine("捕獲到Interrupt異常時線程{0}的狀態:{1}", Thread.CurrentThread.Name, Thread.CurrentThread.ThreadState);
                  }
                  finally 
                  {
                      Console.WriteLine("進入finally語句塊后線程{0}的狀態:{1}", Thread.CurrentThread.Name, Thread.CurrentThread.ThreadState);
                  }
              }

      從代碼中可以看出,當線程調用Interrupted后,它的狀態是已中斷的.這個狀態對于正在執行join,sleep的線程,卻改變了線程的運行結果

      .因為它正在某一對象的休息室中,這時如果它的中斷狀態被改變,那么它就會拋出ThreadInterruptedException異常,意思就是這個線程不能再等待了,其意義就等同于喚醒它了。

      讓我們想象一下我們將一個線程設置了其長達1星期的睡眠時間,有時后必須喚醒它,上述方法就能實現這點

       

       

       

       

      8 細說下Thread 的 Suspend,Resume方法

      Suspend 和Resume方法很奧妙,前者將當前運行的線程掛起,后者能夠恢復當錢被掛起的線程

                  Thread thread1 = new Thread(TestSuspend);
                  Thread thread2 = new Thread(TestSuspend);
                  thread1.Name = "Thread1";
                  thread2.Name = "Thread2";
                  thread1.Start();
                  thread2.Start();
                  //假設在做一些事情
                  
                  Thread.Sleep(1000);
                  Console.WriteLine("需要主線程幫忙了");
      // throw new NullReferenceException("error!"); thread1.Resume(); thread2.Resume(); static void TestSuspend() { Console.WriteLine("Thread:{0} has been suspend!",Thread.CurrentThread.Name); //這里講當前線程掛起 Thread.CurrentThread.Suspend(); Console.WriteLine("{0} has been resume", Thread.CurrentThread.Name); }

      如上代碼,我們制造兩個線程來實現Suspend和Resume的測試,(暫時不考慮臨界區共享同步的問題),TestSuspend方法便是兩個線程的共用方法,

      方法中我們獲取當前運行該方法的線程,然后將其掛起操作,那么假設線程1先掛起了,線程1被中止當前的工作,面壁思過去了,可是這并不影響線程

      2的工作,于是線程2也急匆匆的闖了進來,結果和線程1一樣的悲劇,聰明的你肯定會問,誰能讓線程1和線程2恢復工作?其實有很多方法能讓他們恢

      復工作,但是個人認為,在不創建新線程的條件下,被我們忽視的主線程做不住了,看到自己的兄弟面壁,心里肯定不好受,于是做完他自己的一系列

      事情之后,他便去召喚這2個兄弟回來工作了,可是也許會有這種情況,主線程迫于自己的事情太多太雜而甚至報出了異常, 那么完蛋了,這兩個線程永

      遠無法繼續干活了,或者直接被回收。。。

      這樣這次把他們共享區上鎖,上面部分的代碼保持不變,這樣會發生什么情況呢?

            static void TestSuspend() 
              {
                  lock (lockObj)
                  {
                   。。。。
                  }
              } 

       (由于在TestSuspend方法中加入了鎖,所以每次只允許一個線程工作,大伙不必在本文中深究鎖機制,后續章節會給大家詳細溫故下)

      盡然在thread2.resume()方法上報錯了,仔細分析后發現在thread1離開共享區(testSuspend)方法之后剎那間,thread2進來了,與此同時,主線程

      跑的太快了,導致thread2被掛起前去喚醒thread2,悲劇就這么發生了,其實修改這個bug很容易,只要判斷下線程的狀態,或者主線程中加一個Thread.Sleep()等等,

      但是這種錯誤非常的嚴重,往往在很復雜的業務里讓你發狂,所以微軟決定放棄這兩個方法,將他們歸為過時方法,最后讓大家看下微軟那個深奧的解釋,

      相信看完上述例子后大家都能理解這個含義了

       

       

      9 簡單了解下Thread 的 一些常用的重要屬性

      1 CurrentThread

         獲取到當前線程的對象

      2 IsAlive

         判斷線程是否處于激活狀態

      3 IsBackground

         設置該線程是否是后臺線程,一旦設置true 的話,該線程就被標示為后臺線程

      再次強調下后臺線程的終止不會導致進程的終止

      4 IsThreadPoolThread

          只讀屬性標示該線程是否屬于線程池的托管線程,一般我通過線程池創建的線程該屬性都是true

      5 Name

          獲取到線程的名字,我們可以根據業務或者邏輯來自定義線程的名字

      6 Priority

      這個屬性表示線程的優先級,我們可以用ThreadPriority這個枚舉來設置這個屬性

      ThreadPriority包含有5個優先級大家了解下就行

       

      10  Thread的簡單示例

       在WPF中實現多線程從一個圖片中截取部分圖片

      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;
      using System.Windows;
      using System.Windows.Controls;
      using System.Windows.Data;
      using System.Windows.Documents;
      using System.Windows.Input;
      using System.Windows.Media;
      using System.Windows.Media.Imaging;
      using System.Windows.Navigation;
      using System.Windows.Shapes;
      using System.Drawing;
      using System.Windows.Interop;
      using System.Threading;
      
      namespace ImageFlip
      {
          /// <summary>
          /// WPF  多線程將圖片分割
          /// </summary>
          public partial class MainWindow : Window
          {
              BitmapSource source;
              private object lockObj = new object();
              public MainWindow()
              {
                  InitializeComponent();
                  //首先獲取圖片
                  Bitmap orginalImage = new Bitmap(@"G:\Picture\Tamriel_4E.png");
                  //創建線程1
                  Thread t1 = new Thread(new ParameterizedThreadStart
                      (
                        obj =>
                        {
                            //WPF中使用多線程的話最后一定要返回UI線程,否則操作界面控件時會報錯
                            //BeginInvoke方法便是返回UI線程的方法
                            this.Dispatcher.BeginInvoke((Action)(() => 
                            {
                                //通過Parameter類的屬性裁剪圖片
                                ClipImageAndBind(obj); 
                                //圖片的部分綁定到頁面控件
                                this.TestImage1.Source = source;
                                
                            }));
                        }
                      ));
                  //創建線程2
                  Thread t2 = new Thread(new ParameterizedThreadStart
                  (
                    obj =>
                    {
                        //WPF中使用多線程的話最后一定要返回UI線程,否則操作界面控件時會報錯
                        //BeginInvoke方法便是返回UI線程的方法
                        this.Dispatcher.BeginInvoke((Action)(() =>
                        {
                            //通過Parameter類的屬性裁剪圖片
                            ClipImageAndBind(obj);
                            //圖片的部分綁定到頁面控件
                            this.TestImage2.Source = source;
                            //嘗試將線程1的啟動邏輯放在線程2所持有的方法中
                           // t1.Start(new Parameter { OrginalImage = orginalImage, ClipHeight = 500, ClipWidth = 500, StartX = 0, StartY = 0 });
                        }));
                    }
                  ));
               
                  t2.Start(new Parameter { OrginalImage = orginalImage, ClipHeight = 500, ClipWidth = 500, StartX = orginalImage.Width - 500, StartY = orginalImage.Height - 500 });
                  //嘗試下注釋掉t2.join方法后是什么情況,其實注釋掉之后,兩個線程會一起工作,
                  //去掉注釋后,界面一直到兩個圖片部分都綁定完成后才出現
                  //t2.Join();
                  t1.Start(new Parameter { OrginalImage = orginalImage, ClipHeight = 500, ClipWidth = 500, StartX = 0, StartY = 0 });
              }
      
             /// <summary>
             /// 根據參數類進行剪裁圖片,加鎖防止共享資源被破壞
             /// </summary>
              /// <param name="para">Parameter類對象</param>
              private void ClipImageAndBind(object para)
              {
                  lock (lockObj)
                  {
                      Parameter paraObject = (para as Parameter);
                      source = this.ClipPartOfImage(paraObject);
                      Thread.Sleep(5000);
                  }
              }
      
              /// <summary>
              /// 具體裁剪圖片,大家不必在意這個方法,關鍵是線程的使用
              /// </summary>
              /// <param name="para">Parameter</param>
              /// <returns>部分圖片</returns>
              private BitmapSource ClipPartOfImage(Parameter para)
              {
                  if (para == null) { throw new NullReferenceException("para 不能為空"); }
                  if (para.OrginalImage == null) { throw new NullReferenceException("OrginalImage 不能為空"); }
                  System.Drawing.Rectangle rect = new System.Drawing.Rectangle(para.StartX, para.StartY, para.ClipWidth, para.ClipHeight);
                  var bitmap2 = para.OrginalImage.Clone(rect, para.OrginalImage.PixelFormat) as Bitmap;
                  return ChangeBitmapToBitmapSource(bitmap2);
              }
      
              private BitmapSource ChangeBitmapToBitmapSource(Bitmap bmp)
              {
                  BitmapSource returnSource;
                  try
                  {
                      returnSource = Imaging.CreateBitmapSourceFromHBitmap(bmp.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
                  }
                  catch
                  {
                      returnSource = null;
                  }
                  return returnSource;
              }
      
          }
          
          /// <summary>
          /// 參數類
          /// </summary>
          public class Parameter
          {
              public Bitmap OrginalImage { get; set; }
              public int StartX { get; set; }
              public int StartY { get; set; }
              public int ClipWidth { get; set; }
              public int ClipHeight { get; set; }
          }
      
      }

      前臺界面:

      <Window x:Class="ImageFlip.MainWindow"
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              Title="MainWindow" Height="350" Width="525">
          <Grid>
              <Grid.ColumnDefinitions>
                  <ColumnDefinition></ColumnDefinition>
                  <ColumnDefinition></ColumnDefinition>
              </Grid.ColumnDefinitions>
              <Image x:Name="TestImage1" Grid.Column="0"></Image>
              <Image x:Name="TestImage2" Grid.Column="1"></Image>
          </Grid>
      </Window>

       11 本章總結

       本章介紹了線程一些簡單的基礎知識和對Thread類進行了詳細的介紹,在以后的章節中我會逐步向大家介紹線程同步,異步線程等等有關線程的知識,

      文中估計會有錯誤的地方也請大家海涵并且幫助指出,馬上歐錦賽荷蘭的比賽開始了,祝大家多多鼓勵和關注!

       

      posted @ 2012-06-10 00:48  逆時針の風  閱讀(33455)  評論(75)    收藏  舉報
      主站蜘蛛池模板: 欧洲无码一区二区三区在线观看| 国产精品高清国产三级囯产AV| 亚洲综合在线亚洲优优色| 久久天天躁夜夜躁一区| 国产精品一区二区三区蜜臀| 少妇熟女久久综合网色欲| 99热久久这里只有精品| 精品人妻一区二区三区蜜臀| 麻豆一区二区三区精品蜜桃| 狠狠v日韩v欧美v| 亚洲一区二区三区久久受| 国产剧情91精品蜜臀一区| 國產尤物AV尤物在線觀看| 亚洲欧洲日产国产av无码| 国产一区二区三区黄色片| 国产性色av免费观看| 国产呦交精品免费视频| 成年午夜无码av片在线观看| 国产三级精品三级| 亚洲二区中文字幕在线| 妖精视频亚州无吗高清版| 亚洲欧美在线一区中文字幕| 日本55丰满熟妇厨房伦| 日韩人妻精品中文字幕专区 | 中文成人在线| 日韩有码中文字幕一区二区| 日韩精品无码一区二区视频| 女人张开腿无遮无挡视频| 久久久久夜夜夜精品国产| 久9re热视频这里只有精品免费| 欧洲无码一区二区三区在线观看| 精品人妻av区乱码| 亚洲欧洲一区二区精品| 国产精品一二二区视在线| 青草青草久热精品视频在线观看 | 亚洲一区中文字幕人妻| 真实国产老熟女无套中出| 国产美女直播亚洲一区色| 国产精品一区二区三区蜜臀| 在线看av一区二区三区| 亚洲人成色77777|