程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> C#中級-常用多線程操作(持續更新),

C#中級-常用多線程操作(持續更新),

編輯:關於.NET

C#中級-常用多線程操作(持續更新),


一、前言

      多線程操作一直是編程的常用操作,掌握好基本的操作可以讓程序運行的更加有效。本文不求大而全,只是將我自己工作中常常用到的多線程操作做個分類和總結。平時記性不好的時候還能看看。本文參考了多篇園子裡的精彩博文,在文章最後會貼出具體來源,感謝他們的無私奉獻。

 

二、關於線程

(1) 為何使用線程:

      可以使用線程將代碼同其他代碼隔離,提高應用程序的可靠性;

      可以使用線程來簡化編碼;

      可以使用線程來實現並發執行。

(2) 進程、應用程序域以及線程的關系:

      進程(Process)是Windows系統中的一個基本概念,它包含著一個運行程序所需要的資源。進程之間是相對獨立的,一個進程無法訪問另一個進程的數據(除非利用分布式計算方式),一個進程運行的失敗也不會影響其他進程的運行,Windows系統就是利用進程把工作劃分為多個獨立的區域的。進程可以理解為一個程序的基本邊界。

      應用程序域(AppDomain)是一個程序運行的邏輯區域,它可以視為一個輕量級的進程,.NET的程序集正是在應用程序域中運行的,一個進程可以包含有多個應用程序域,一個應用程序域也可以包含多個程序集。在一個應用程序域中包含了一個或多個上下文context,使用上下文CLR就能夠把某些特殊對象的狀態放置在不同容器當中。

      線程(Thread)是進程中的基本執行單元,在進程入口執行的第一個線程被視為這個進程的主線程。

      關系圖如下:

三、Thread

      Thread可能是除了Task之外用的最多的多線程類。一般用法:

// one thread
Thread thread = new ThreadStart(functiion);
thread.Start();

// thread.join
Thread ThreadA = new Thread(delegate()
{
  //do something      
});
  
Thread ThreadB = new Thread(delegate()
{       
  //do something;  
  
      ThreadA.Join();
  
  //do another thing      
});
  
//啟動線程
ThreadA.Start();
ThreadB.Start();

//一開始,兩個線程相互交替運行,當線程B運行到線程A的join時,會先讓線程A執行完,然後線程B再繼續執行。你可以理解為超車,一開始兩者互不相讓,當join時,線程A超車了線程B

 

四、ThreadPool

        由於線程的創建和銷毀需要耗費一定的開銷,過多的使用線程會造成內存資源的浪費,出於對性能的考慮,於是引入了線程池的概念。線程池維護一個請求隊列,線程池的代碼從隊列提取任務,然後委派給線程池的一個線程執行,線程執行完不會被立即銷毀,這樣既可以在後台執行任務,又可以減少線程創建和銷毀所帶來的開銷。

       如果一個線程的時間非常長,就沒必要用線程池了(不是不能作長時間操作,而是不宜。),況且我們還不能控制線程池中線程的開始、掛起、和中止。

ThreadPool.QueueUserWorkItem(function,parameter);

 

五、Task

      Task是我用的最多的多線程方式,一般使用的方法如下:  

// one task
var task = new Task(() =>{
// do something
}); task.Start();
// task one by one
var task = new Task(() =>{
 // do something  
});

Task task2 = task.ContinueWith(()=>{
// do something  
});
task.Start();

// many tasks var tasks = new Task[PackCount]; //多線程任務 for (int index = 0; index < PackCount; index++) { int Threadindex = index; var task = new Task(() => { // do something } }); tasks[Threadindex] = task; task.Start(); } Task.WaitAll(tasks); //等待所有線程完成 //Task.WaitAny(tasks); //等待一個線程完成繼續執行主程序

//Task.Factory
Task.Factory.StartNew(()=>{ // do something });

 

六、Invoke、BeginInvoke、DynamicInvoke

      此處主要說明的是delegate下的各種Invoke

      Invoke (委托方法執行在調用處同一個線程中

delegate void MyDelegate();

MyDelegate del = new MyDelegate(Function);
del .Invoke();     //使用到委托的invoke方法

 

      BeginInvoke(它從線程池中抓取一個空閒線程,來委托執行方法)

A情況:使用IAsyncResult.IsCompleted判斷子線程是否執行完畢

delegate T MyDelegate();

MyDelegate del = new MyDelegate(Function);

IAsyncResult result = del.BeginInvoke(parameter,null,null);

//if the branch thread is not completed
while(!result.IsCompleted)  
{
    // the main thread do another thing
}

T data = del.EndInvoke(result); 
// var data is the result of Function with parameter

 

B情況:使用IAsyncResult.AsyncWaitHandle.WaitOne(timeout)判斷子線程是否執行完畢

delegate T MyDelegate();

MyDelegate del = new MyDelegate(Function);

IAsyncResult result = del.BeginInvoke(parameter,null,null);

//if the branch thread is not completed
while(!result.AsyncWaitHandle.WaitOne(int timeout))  
{
    // the main thread do another thing
}

T data = del.EndInvoke(result); 
// var data is the result of Function with parameter

 

C情況:使用WaitHandle.WaitAll(WaitHandle[],timeout)判斷子線程是否執行完畢

delegate T MyDelegate();

MyDelegate del = new MyDelegate(Function);

IAsyncResult result = del.BeginInvoke(parameter,null,null);

WaitHandle[] waitHandleList = new WaitHandle[] { result.AsyncWaitHandle,........ };

while (!WaitHandle.WaitAll(waitHandleList,int timeout))
{
    // the main thread do another thing
}

T data = del.EndInvoke(result); 
// var data is the result of Function with parameter

 

D情況:使用輪詢方式來檢測異步方法的狀態非常麻煩,而且效率不高,為此需要使用回調函數。主線程可以安心做自己的事,而異步線程完成操作後執行回調函數即可。回調函數依然是在異步線程上,而非主線程上。

delegate T MyDelegate();

MyDelegate del = new MyDelegate(Function);

IAsyncResult result = del.BeginInvoke(parameter,new AsyncCallback(callbackFunction),object);

....MainThread do somethng...

static void callbackFunction(IAsyncResult result)
{
     AsyncResult  _result = (AsyncResult )result;

     MyDelegate del = (MyDelegate)_result.AsyncDelegate;

     T data = del.EndInvoke(_result);

     T1 objectReciever = (T1)result.AsyncResult; //object=result.AsyncResult
}

 

        DynamicInvoke:

       與Delegate.Invoke類似,同步,且同線程,唯一不同的是,是采用後期綁定的方式來調用委托方法,所以時間代價較大。

工作實例:在WPF中出現一種異常:“調用線程無法訪問此對象,因為另一個線程擁有該對象。

情況A:假設發生該異常的代碼是在xaml.cs文件中,那麼Dispatcher.Invoke已經夠用了。

情況B: 假設發生該異常的代碼是在.cs文件中,那麼在Stack Overflow上有一招:

 1 private void RaiseEventOnUIThread(Delegate theEvent, object[] args)
 2 {
 3       foreach (Delegate d in theEvent.GetInvocationList())
 4       {
 5             ISynchronizeInvoke syncer = d.Target as ISynchronizeInvoke;
 6             if (syncer == null)  //靜態函數為null
7 { 8 d.DynamicInvoke(args); 9 } 10 else 11 { 12 syncer.BeginInvoke(d, args); //在創建了此對象的線程上異步執行委托 13 } 14 } 15 }

    

References:

【1】http://blog.sina.com.cn/s/blog_5a6f39cf0100qtzf.html

【2】http://www.cnblogs.com/slikyn/articles/1525940.html

【3】http://kb.cnblogs.com/page/130487/#t3

【4】http://blog.csdn.net/soft_123456/article/details/38819877

【5】http://www.cnblogs.com/laoyur/archive/2011/04/14/2016025.html

【6】http://blog.csdn.net/cselmu9/article/details/8274556

【7】http://stackoverflow.com/questions/1698889/raise-events-in-net-on-the-main-ui-thread

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved