亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C# > 正文

解析C#多線程編程中異步多線程的實現及線程池的使用

2020-01-24 01:14:51
字體:
來源:轉載
供稿:網友

0、線程的本質
線程不是一個計算機硬件的功能,而是操作系統提供的一種邏輯功能,線程本質上是進程中一段并發運行的代碼,所以線程需要操作系統投入CPU資源來運行和調度。

1、多線程:

使用多個處理句柄同時對多個任務進行控制處理的一種技術。據博主的理解,多線程就是該應用的主線程任命其他多個線程去協助它完成需要的功能,并且主線程和協助線程是完全獨立進行的。不知道這樣說好不好理解,后面慢慢在使用中會有更加詳細的講解。

2、多線程的使用:

(1)最簡單、最原始的使用方法:Thread oGetArgThread = new Thread(new ThreadStart(() =>{});這種用法應該大多數人都使用過,參數為一個ThreadStart類型的委托。將ThreadStart轉到定義可知:

public delegate void ThreadStart();

它是一個沒有參數,沒有返回值的委托。所以他的使用如下:

static void Main(string[] args){   Thread oGetArgThread = new Thread(new ThreadStart(Test));  oGetArgThread.IsBackground = true;  oGetArgThread.Start();      for (var i = 0; i < 1000000; i++)    {      Console.WriteLine("主線程計數" + i);      //Thread.Sleep(100);    }}private static void Test() {    for (var i = 0; i < 1000000; i++)    {      Console.WriteLine("后臺線程計數" + i);      //Thread.Sleep(100);    } }定義一個沒有參數沒有返回值的方法傳入該委托。當然也可以不定義方法寫成匿名方法:    static void Main(string[] args)    {      Thread oGetArgThread = new Thread(new System.Threading.ThreadStart(() =>      {                for (var i = 0; i < 1000000; i++)        {          Console.WriteLine("后臺線程計數" + i);          //Thread.Sleep(100);        }      }));      oGetArgThread.IsBackground = true;      oGetArgThread.Start();

這個和上面的意義相同。得到的結果如下:

201636180800984.png (677×442)

說明主線程和后臺線程是互相獨立的。由系統調度資源去執行。

如果這樣那有人就要問了,如果我需要多線程執行的方法有參數或者有返回值或者既有參數又有返回值呢。。。別著急我們來看看new Thread()的幾個構造函數:

public Thread(ParameterizedThreadStart start);    public Thread(ThreadStart start);    public Thread(ParameterizedThreadStart start, int maxStackSize);    public Thread(ThreadStart start, int maxStackSize);

轉到定義可知參數有兩類,一類是無參無返回值的委托,另一類是有參無返回值的委托。對于有參數的委托使用方法:

    static void Main(string[] args)    {      Thread oThread = new Thread(new ParameterizedThreadStart(Test2));         oThread.IsBackground = true;      oThread.Start(1000);     }     private static void Test2(object Count)    {      for (var i = 0; i < (int)Count; i++)      {        Console.WriteLine("后臺線程計數" + i);        //Thread.Sleep(100);      }    }  

 

對于有參又有返回值的委托,很顯然使用new Thread()這種方式是沒有解決方案的。其實對于有參又有返回值的委托可以使用異步來實現:

public delegate string MethodCaller(string name);//定義個代理 MethodCaller mc = new MethodCaller(GetName); string name = "my name";//輸入參數 IAsyncResult result = mc.BeginInvoke(name,null, null); string myname = mc.EndInvoke(result);//用于接收返回值  public string GetName(string name)  // 函數{  return name;}  

關于這種方式還有幾點值得一說的是:

Thread oGetArgThread = new Thread(new ThreadStart(Test));oGetArgThread.Join();//主線程阻塞,等待分支線程運行結束,這一步看功能需求進行選擇,主要為了多個進程達到同步的效果

②線程的優先級可以通過Thread對象的Priority屬性來設置,Priority屬性對應一個枚舉:

public enum ThreadPriority  {    // 摘要:     //   可以將 System.Threading.Thread 安排在具有任何其他優先級的線程之后。    Lowest = 0,    //    // 摘要:     //   可以將 System.Threading.Thread 安排在具有 Normal 優先級的線程之后,在具有 Lowest 優先級的線程之前。    BelowNormal = 1,    //    // 摘要:     //   可以將 System.Threading.Thread 安排在具有 AboveNormal 優先級的線程之后,在具有 BelowNormal 優先級的線程之前。    //   默認情況下,線程具有 Normal 優先級。    Normal = 2,    //    // 摘要:     //   可以將 System.Threading.Thread 安排在具有 Highest 優先級的線程之后,在具有 Normal 優先級的線程之前。    AboveNormal = 3,    //    // 摘要:     //   可以將 System.Threading.Thread 安排在具有任何其他優先級的線程之前。    Highest = 4,  }

從0到4,優先級由低到高。

③關于多個線程同時使用一個對象或資源的情況,也就是線程的資源共享,為了避免數據紊亂,一般采用.Net悲觀鎖lock的方式處理。

     private static object oLock = new object();    private static void Test2(object Count)    {      lock (oLock)      {        for (var i = 0; i < (int)Count; i++)        {          Console.WriteLine("后臺線程計數" + i);          //Thread.Sleep(100);        }      }    }

 

(2)Task方式使用多線程:

這種方式一般用在需要循環處理某項業務并且需要得到處理后的結果。使用代碼如下:

List<Task> lstTaskBD = new List<Task>();foreach (var bd in lstBoards)  {     var bdTmp = bd;//這里必須要用一個臨時變量     var oTask = Task.Factory.StartNew(() =>     {       var strCpBdCmd = "rm -Rf " + bdTmp.Path + "/*;cp -R " + CombineFTPPaths(FTP_EMULATION_BD_ROOT,"bd_correct") + "/* " + bdTmp.Path + "/";       oPlink.Run(bdTmp.EmulationServer.BigIP, bdTmp.EmulationServer.UserName, bdTmp.EmulationServer.Password,strCpBdCmd);       Thread.Sleep(500);      });      lstTaskBD.Add(oTask);  }Task.WaitAll(lstTaskBD.ToArray());//等待所有線程只都行完畢

使用這種方式的時候需要注意這一句 var bdTmp = bd;這里必須要用一個臨時變量,要不然多個bd對象容易串數據。如果有興趣可以調試看看。這種方法比較簡單,就不多說了。當然Task對象的用法肯定遠不止如此,還涉及到任務的調度等復雜的邏輯。博主對這些東西理解有限,就不講解了。

 (3)異步操作的本質
  所有的程序最終都會由計算機硬件來執行,所以為了更好的理解異步操作的本質,我們有必要了解一下它的硬件基礎。 熟悉電腦硬件的朋友肯定對DMA這個詞不陌生,硬盤、光驅的技術規格中都有明確DMA的模式指標,其實網卡、聲卡、顯卡也是有DMA功能的。DMA就是直 接內存訪問的意思,也就是說,擁有DMA功能的硬件在和內存進行數據交換的時候可以不消耗CPU資源。只要CPU在發起數據傳輸時發送一個指令,硬件就開 始自己和內存交換數據,在傳輸完成之后硬件會觸發一個中斷來通知操作完成。這些無須消耗CPU時間的I/O操作正是異步操作的硬件基礎。所以即使在DOS 這樣的單進程(而且無線程概念)系統中也同樣可以發起異步的DMA操作。

(4)異步操作的優缺點
  因為異步操作無須額外的線程負擔,并且使用回調的方式進行處理,在設計良好的情況下,處理函數可以不必使用共享變量(即使無法完全不用,最起碼可以減少 共享變量的數量),減少了死鎖的可能。當然異步操作也并非完美無暇。編寫異步操作的復雜程度較高,程序主要使用回調方式進行處理,與普通人的思維方式有些出入,而且難以調試。

3、線程池的用法:

一般由于考慮到服務器的性能等問題,保證一個時間段內系統線程數量在一定的范圍,需要使用線程池的概念。大概用法如下:

  public class CSpiderCtrl  {     //將線程池對象作為一個全局變量    static Semaphore semaphore;    public static void Run()    {      //1. 創建 SuperLCBB客戶端對象      var oClient = new ServiceReference_SuperLCBB.SOAServiceClient();       //2.初始化的時候new最大的線程池個數255(這個數值根據實際情況來判斷,如果服務器上面的東西很少,則可以設置大點)      semaphore = new Semaphore(250, 255);      CLogService.Instance.Debug("又一輪定時采集...");      _TestBedGo(oClient);    }    //執行多線程的方法   private static void _TestBedGo(ServiceReference_SuperLCBB.SOAServiceClient oClient)    {      List<string> lstExceptPDUs = new List<string>(){        "SUPERLABEXP"      };      var oTestBedRes = oClient.GetTestBedExceptSomePDU(lstExceptPDUs.ToArray(), true);      if (CKVRes.ERRCODE_SUCCESS != oTestBedRes.ErrCode)      {        CLogService.Instance.Error("xxx");        return;      }      var lstTestBed = oTestBedRes.ToDocumentsEx();      System.Threading.Tasks.Parallel.ForEach(lstTestBed, (oTestBed) =>      {         //一次最多255個線程,超過255的必須等待線程池釋放一個線程出來才行        semaphore.WaitOne();        //CLogService.Instance.Info("開始采集測試床:" + oTestBed[TBLTestBed.PROP_NAME]);        //Thread.Sleep(2000);        var strTestBedName = oTestBed[TBLTestBed.PROP_NAME] as string;        var strSuperDevIP = oTestBed[TBLTestBed.PROP_SUPERDEVIP] as string;        var strTestBedGID = oTestBed[TBLTestBed.PROP_GID] as string;        var strPdu = oTestBed[TBLTestBed.PROP_PDUGID] as string;        Thread.Sleep(new Random().Next(1000, 5000));        var oGetRootDevicesByTestBedGIDRes = oClient.GetRootDevicesByTestBedGID(strTestBedGID);        CLogService.Instance.Debug(strPdu + "――測試床Name:" + strTestBedName + "開始");        Stopwatch sp = new Stopwatch();        sp.Start();        if (oGetRootDevicesByTestBedGIDRes.ErrCode != CKVRes.ERRCODE_SUCCESS || oGetRootDevicesByTestBedGIDRes.Documents.Count < 2)        {          CLogService.Instance.Debug("shit -- 3實驗室中測試床Name:" + strTestBedName + "2完成異常0");       //這里很重要的一點,每一次return 前一定要記得釋放線程,否則這個一直會占用資源          semaphore.Release();          return;        }        var strXML = oGetRootDevicesByTestBedGIDRes.Documents[0];        var strExeName = oGetRootDevicesByTestBedGIDRes.Documents[1];        //var strExeName = "RateSpider";        var oSuperDevClient = new SuperDevClient(CSuperDev.ENDPOINT, string.Format(CSuperDev.SuperDevURL, strSuperDevIP));        try        {          oSuperDevClient.IsOK();        }        catch (Exception)        {          CLogService.Instance.Error("測試床Name:" + strTestBedName + "異常,插件沒起");          semaphore.Release();          return;        }        //2.3.1.請求SuperDev.Server(SuperDevIP),發送Run(XML和Exename)        var oRunExeRes = new CKVRes();        try        {          oRunExeRes = oSuperDevClient.RunExeEx(strExeName, false, new string[] { strXML });        }        catch        {          //CLogService.Instance.Debug("測試床Name:" + strTestBedName + "異常:" + ex.Message);        }        sp.Stop();        CLogService.Instance.Debug(strPdu + "――測試床Name:" + strTestBedName + "完成時間" + sp.Elapsed);          //每一個線程完畢后記得釋放資源        semaphore.Release();      });    }  }

需要注意:Semaphore對象的數量需要根據服務器的性能來設定;System.Threading.Tasks.Parallel.ForEach這種方式表示同時啟動lstTestBed.Length個線程去做一件事情,可以理解為

foreach(var oTestbed in lstTestBed){    Thread oThread=new Thread(new ThreadStart({  ...}));     }

 

(4) 多線程里面還有一個值得一說的SpinWait類,用于提供對基于自旋的等待的支持。也就是說支持重復執行一個委托,知道滿足條件就返回,我們來看它的用法:

    public static void SpinUntil(Func<bool> condition);       public static bool SpinUntil(Func<bool> condition, int millisecondsTimeout);       public static bool SpinUntil(Func<bool> condition, TimeSpan timeout);

這個方法有三個構造函數,后兩個需要傳入一個時間,表示如果再規定的時間內還沒有返回則自動跳出,防止死循環。

            SpinWait.SpinUntil(() =>          {            bIsworking = m_oClient.isworking(new isworking()).result;            return bIsworking == false;          }, 600000);          //如果等了10分鐘還在跳纖則跳出          if (bIsworking)          {            oRes.ErrCode = "false交換機跳纖時間超過10分鐘,請檢查異常再操作";            return oRes;          }

4、多線程的優缺點
多線程的優點很明顯,線程中的處理程序依然是順序執行,符合普通人的思維習慣,所以編程簡單。但是多線程的缺點也同樣明顯,線程的使用(濫用)會給系統帶來上下文切換的額外負擔。并且線程間的共享變量可能造成死鎖的出現。

5、適用范圍
在了解了線程與異步操作各自的優缺點之后,我們可以來探討一下線程和異步的合理用途。我認為:當需要執行I/O操作時,使用異步操作比使用線程+同步 I/O操作更合適。I/O操作不僅包括了直接的文件、網絡的讀寫,還包括數據庫操作、Web Service、HttpRequest以及.net Remoting等跨進程的調用。

而線程的適用范圍則是那種需要長時間CPU運算的場合,例如耗時較長的圖形處理和算法執行。但是往往由于使用線程編程的簡單和符合習慣,所以很多朋友往往會使用線程來執行耗時較長的I/O操作。這樣在只有少數幾個并發操作的時候還無傷大雅,如果需要處理大量的并發操作時就不合適了。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
91精品国产自产91精品| 亚洲男人的天堂在线播放| 亚洲成人av中文字幕| 亚洲一级一级97网| 久久最新资源网| 久久亚洲精品小早川怜子66| 亚洲精品www久久久| 国产欧美韩国高清| 欧美日本在线视频中文字字幕| 亚洲激情在线观看视频免费| 国产精品天天狠天天看| 国产欧美日韩免费| 欧美电影第一页| 欧美激情区在线播放| 性色av一区二区三区红粉影视| 曰本色欧美视频在线| 国产精品狼人色视频一区| 国产精品丝袜一区二区三区| 亚洲黄色成人网| 久久精品国产96久久久香蕉| 国产午夜精品美女视频明星a级| 欧美黄色三级网站| 国产亚洲欧美视频| 亚洲一区二区少妇| 久久高清视频免费| 中文字幕欧美亚洲| 国产丝袜高跟一区| 欧美电影在线免费观看网站| 国产999在线观看| 亚洲精品二三区| 日韩av中文在线| 亚洲精品天天看| 日韩av电影在线播放| 久久久www成人免费精品| 这里只有精品视频| 性金发美女69hd大尺寸| 国产精品九九久久久久久久| 欧美性xxxx极品hd欧美风情| 国产伊人精品在线| 国产经典一区二区| 最近2019中文字幕第三页视频| 亚洲人成五月天| 国产精品久久久久久久久久99| 国产精品三级网站| 国产综合视频在线观看| 操日韩av在线电影| 日韩免费高清在线观看| 日本高清不卡的在线| 国产精品国语对白| 成人免费大片黄在线播放| 亚洲成成品网站| 国产亚洲综合久久| 欧美重口另类videos人妖| 中文欧美在线视频| 一区二区欧美日韩视频| 国产精品99久久久久久久久| 亚洲精品电影在线| 日韩69视频在线观看| 69久久夜色精品国产69| 久久久久久久久国产| 久久欧美在线电影| 国产免费观看久久黄| 亚洲国产小视频在线观看| 亚洲欧美变态国产另类| 国产色视频一区| 久久九九全国免费精品观看| 啪一啪鲁一鲁2019在线视频| 国产精品91久久久| 亚洲欧美色婷婷| 欧美日韩一区二区在线| 日日噜噜噜夜夜爽亚洲精品| 国产精品一香蕉国产线看观看| 在线观看亚洲区| 欧美日韩中国免费专区在线看| 久久69精品久久久久久国产越南| 茄子视频成人在线| 亚洲男人第一av网站| 国产又爽又黄的激情精品视频| 国产精品视频免费观看www| 国产xxx69麻豆国语对白| yellow中文字幕久久| 97人洗澡人人免费公开视频碰碰碰| 中文字幕国产精品久久| 国产精品免费小视频| 亚洲黄色有码视频| 久久久久免费精品国产| 成人精品网站在线观看| 亚洲аv电影天堂网| 97在线视频免费播放| 国产精品九九久久久久久久| 成人免费高清完整版在线观看| 欧美日韩在线看| 最近的2019中文字幕免费一页| 欧美夫妻性生活xx| 精品国产成人av| 日韩精品极品视频| 亚洲欧洲第一视频| 黄色精品在线看| 精品亚洲一区二区三区在线观看| 亚洲精品av在线| 日韩av不卡电影| 一区二区三区www| 国产成人高潮免费观看精品| 亚洲午夜久久久久久久| 国产主播欧美精品| 欧美日韩国产精品一区| 亚洲专区在线视频| 亚洲精品456在线播放狼人| 中文字幕亚洲国产| 欧美在线中文字幕| 88国产精品欧美一区二区三区| 国产精品专区h在线观看| 欧美激情中文字幕在线| 98精品在线视频| 91九色国产社区在线观看| 国产99久久久欧美黑人| 欧美一区视频在线| 精品久久久久久久大神国产| 精品视频在线导航| 国产日韩av在线| 亚洲天堂一区二区三区| 神马久久桃色视频| 北条麻妃一区二区三区中文字幕| 国产精品自在线| 夜夜嗨av色一区二区不卡| 亚洲国产中文字幕久久网| 色播久久人人爽人人爽人人片视av| 97久久久免费福利网址| 夜夜嗨av一区二区三区四区| 亚洲欧美中文日韩在线| 在线视频日本亚洲性| 欧美与欧洲交xxxx免费观看| 国产精品91久久| 午夜欧美大片免费观看| 丝袜一区二区三区| 在线免费看av不卡| 国产97在线观看| 久久99国产精品自在自在app| 欧美视频在线视频| 热re99久久精品国产66热| 日本成人精品在线| 欧美极品少妇与黑人| 在线播放国产一区二区三区| 久久影视电视剧免费网站| 国产精品久久久久久久电影| 91九色国产在线| 亚洲色图15p| 亚洲午夜未删减在线观看| 色偷偷噜噜噜亚洲男人的天堂| 福利一区视频在线观看| www.欧美精品| 91国产视频在线| 亚洲www永久成人夜色| 欧美激情精品久久久| 日本一区二三区好的精华液| 欧美大片在线免费观看| 96精品视频在线| 日韩免费电影在线观看| 欧美精品在线免费播放| 国内成人精品一区| 成人国产亚洲精品a区天堂华泰| 亚洲美女黄色片| 亲子乱一区二区三区电影| 国产中文欧美精品|