最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C#多線程學(xué)習(xí)之(三)生產(chǎn)者和消費(fèi)者用法分析

 更新時(shí)間:2015年04月07日 09:55:37   作者:樂樂010  
這篇文章主要介紹了C#多線程學(xué)習(xí)之生產(chǎn)者和消費(fèi)者用法,實(shí)例分析了C#中線程沖突的原理與資源分配的技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下

本文實(shí)例講述了C#多線程學(xué)習(xí)之生產(chǎn)者和消費(fèi)者用法。分享給大家供大家參考。具體實(shí)分析如下:

前面的文章說過,每個(gè)線程都有自己的資源,但是代碼區(qū)是共享的,即每個(gè)線程都可以執(zhí)行相同的函數(shù)。這可能帶來的問題就是幾個(gè)線程同時(shí)執(zhí)行一個(gè)函數(shù),導(dǎo)致數(shù)據(jù)的混亂,產(chǎn)生不可預(yù)料的結(jié)果,因此我們必須避免這種情況的發(fā)生。

C#提供了一個(gè)關(guān)鍵字lock,它可以把一段代碼定義為互斥段(critical section),互斥段在一個(gè)時(shí)刻內(nèi)只允許一個(gè)線程進(jìn)入執(zhí)行,而其他線程必須等待。在C#中,關(guān)鍵字lock定義如下:

lock(expression) statement_block 

expression代表你希望跟蹤的對(duì)象,通常是對(duì)象引用。
如果你想保護(hù)一個(gè)類的實(shí)例,一般地,你可以使用this;
如果你想保護(hù)一個(gè)靜態(tài)變量(如互斥代碼段在一個(gè)靜態(tài)方法內(nèi)部),一般使用類名就可以了。

而statement_block就是互斥段的代碼,這段代碼在一個(gè)時(shí)刻內(nèi)只可能被一個(gè)線程執(zhí)行。

下面是一個(gè)使用lock關(guān)鍵字的典型例子,在注釋里說明了lock關(guān)鍵字的用法和用途。
示例如下:

using System;
using System.Threading;
namespace ThreadSimple
{
 internal class Account 
 {
  int balance;
  Random r = new Random();
  internal Account(int initial) 
  {
   balance = initial;
  } 
  internal int Withdraw(int amount) 
  {
   if (balance < 0)
   {
    //如果balance小于0則拋出異常
    throw new Exception("Negative Balance");
   }
   //下面的代碼保證在當(dāng)前線程修改balance的值完成之前
   //不會(huì)有其他線程也執(zhí)行這段代碼來修改balance的值
   //因此,balance的值是不可能小于0 的
   lock (this)
   {
    Console.WriteLine("Current Thread:"+Thread.CurrentThread.Name);
    //如果沒有l(wèi)ock關(guān)鍵字的保護(hù),那么可能在執(zhí)行完if的條件判斷之后
    //另外一個(gè)線程卻執(zhí)行了balance=balance-amount修改了balance的值
    //而這個(gè)修改對(duì)這個(gè)線程是不可見的,所以可能導(dǎo)致這時(shí)if的條件已經(jīng)不成立了
    //但是,這個(gè)線程卻繼續(xù)執(zhí)行balance=balance-amount,所以導(dǎo)致balance可能小于0
    if (balance >= amount) 
    {
     Thread.Sleep(5);
     balance = balance - amount;
     return amount;
    } 
    else 
    {
     return 0; // transaction rejected
     }
   }
  }
  internal void DoTransactions() 
  {
   for (int i = 0; i < 100; i++) 
   Withdraw(r.Next(-50, 100));
  }
 } 

 internal class Test 
 {
  static internal Thread[] threads = new Thread[10];
  public static void Main() 
  {
   Account acc = new Account (0);
   for (int i = 0; i < 10; i++) 
   {
    Thread t = new Thread(new ThreadStart(acc.DoTransactions));
    threads[i] = t;
   }
   for (int i = 0; i < 10; i++) 
    threads[i].Name=i.ToString();
   for (int i = 0; i < 10; i++) 
    threads[i].Start();
   Console.ReadLine();
  }
 }
}

Monitor 類鎖定一個(gè)對(duì)象

當(dāng)多線程公用一個(gè)對(duì)象時(shí),也會(huì)出現(xiàn)和公用代碼類似的問題,這種問題就不應(yīng)該使用lock關(guān)鍵字了,這里需要用到System.Threading中的一個(gè)類Monitor,我們可以稱之為監(jiān)視器,Monitor提供了使線程共享資源的方案。

Monitor類可以鎖定一個(gè)對(duì)象,一個(gè)線程只有得到這把鎖才可以對(duì)該對(duì)象進(jìn)行操作。對(duì)象鎖機(jī)制保證了在可能引起混亂的情況下一個(gè)時(shí)刻只有一個(gè)線程可以訪問這個(gè)對(duì)象。
Monitor必須和一個(gè)具體的對(duì)象相關(guān)聯(lián),但是由于它是一個(gè)靜態(tài)的類,所以不能使用它來定義對(duì)象,而且它的所有方法都是靜態(tài)的,不能使用對(duì)象來引用。下面代碼說明了使用Monitor鎖定一個(gè)對(duì)象的情形:

......
Queue oQueue=new Queue();
......
Monitor.Enter(oQueue);
......//現(xiàn)在oQueue對(duì)象只能被當(dāng)前線程操縱了
Monitor.Exit(oQueue);//釋放鎖

如上所示,當(dāng)一個(gè)線程調(diào)用Monitor.Enter()方法鎖定一個(gè)對(duì)象時(shí),這個(gè)對(duì)象就歸它所有了, 其它線程想要訪問這個(gè)對(duì)象,只有等待它使用Monitor.Exit()方法釋放鎖。為了保證線程最終都能釋放鎖,你可以把Monitor.Exit() 方法寫在try-catch-finally結(jié)構(gòu)中的finally代碼塊里。

對(duì)于任何一個(gè)被Monitor鎖定的對(duì)象,內(nèi)存中都保存著與它相關(guān)的一些信息:
其一是現(xiàn)在持有鎖的線程的引用;
其二是一個(gè)預(yù)備隊(duì)列,隊(duì)列中保存了已經(jīng)準(zhǔn)備好獲取鎖的線程;
其三是一個(gè)等待隊(duì)列,隊(duì)列中保存著當(dāng)前正在等待這個(gè)對(duì)象狀態(tài)改變的隊(duì)列的引用。

當(dāng)擁有對(duì)象鎖的線程準(zhǔn)備釋放鎖時(shí),它使用Monitor.Pulse()方法通知等待隊(duì)列中的第一個(gè)線程,于是該線程被轉(zhuǎn)移到預(yù)備隊(duì)列中,當(dāng)對(duì)象鎖被釋放時(shí),在預(yù)備隊(duì)列中的線程可以立即獲得對(duì)象鎖。

下面是一個(gè)展示如何使用lock關(guān)鍵字和Monitor類來實(shí)現(xiàn)線程的同步和通訊的例子,也是一個(gè)典型的生產(chǎn)者與消費(fèi)者問題。
這個(gè)例程中,生產(chǎn)者線程和消費(fèi)者線程是交替進(jìn)行的,生產(chǎn)者寫入一個(gè)數(shù),消費(fèi)者立即讀取并且顯示(注釋中介紹了該程序的精要所在)。

用到的系統(tǒng)命名空間如下:

using System;
using System.Threading;

首先,定義一個(gè)被操作的對(duì)象的類Cell,在這個(gè)類里,有兩個(gè)方法:ReadFromCell()和 WriteToCell。消費(fèi)者線程將調(diào)用ReadFromCell()讀取cellContents的內(nèi)容并且顯示出來,生產(chǎn)者進(jìn)程將調(diào)用 WriteToCell()方法向cellContents寫入數(shù)據(jù)。

示例如下:

public class Cell
{
  int cellContents; // Cell對(duì)象里邊的內(nèi)容
  bool readerFlag = false; // 狀態(tài)標(biāo)志,為true時(shí)可以讀取,為false則正在寫入
  public int ReadFromCell( )
  {
   lock(this) // Lock關(guān)鍵字保證了什么,請(qǐng)大家看前面對(duì)lock的介紹
   {
    if (!readerFlag)//如果現(xiàn)在不可讀取
    { 
     try
     {
      //等待WriteToCell方法中調(diào)用Monitor.Pulse()方法
      Monitor.Wait(this);
     }
     catch (SynchronizationLockException e)
     {
      Console.WriteLine(e);
     }
     catch (ThreadInterruptedException e)
     {
      Console.WriteLine(e);
     }
    }
    Console.WriteLine("Consume: {0}",cellContents);
    readerFlag = false;
    //重置readerFlag標(biāo)志,表示消費(fèi)行為已經(jīng)完成
    Monitor.Pulse(this); 
    //通知WriteToCell()方法(該方法在另外一個(gè)線程中執(zhí)行,等待中)
   }
   return cellContents;
  }
  public void WriteToCell(int n)
  {
   lock(this)
   {
    if (readerFlag)
    {
     try
     {
      Monitor.Wait(this);
     }
     catch (SynchronizationLockException e)
     {
      //當(dāng)同步方法(指Monitor類除Enter之外的方法)在非同步的代碼區(qū)被調(diào)用
      Console.WriteLine(e);
     }
     catch (ThreadInterruptedException e)
     {
       //當(dāng)線程在等待狀態(tài)的時(shí)候中止 
      Console.WriteLine(e);
     }
    }
    cellContents = n;
    Console.WriteLine("Produce: {0}",cellContents);
    readerFlag = true; 
    Monitor.Pulse(this); 
    //通知另外一個(gè)線程中正在等待的ReadFromCell()方法
   }
  }
}

下面定義生產(chǎn)者類 CellProd 和消費(fèi)者類 CellCons ,它們都只有一個(gè)方法ThreadRun(),以便在Main()函數(shù)中提供給線程的ThreadStart代理對(duì)象,作為線程的入口。

public class CellProd
{
 Cell cell; // 被操作的Cell對(duì)象
 int quantity = 1; // 生產(chǎn)者生產(chǎn)次數(shù),初始化為1 
 public CellProd(Cell box, int request)
 {
  //構(gòu)造函數(shù)
  cell = box; 
  quantity = request; 
 }
 public void ThreadRun( )
 {
  for(int looper=1; looper<=quantity; looper++)
  cell.WriteToCell(looper); //生產(chǎn)者向操作對(duì)象寫入信息
 }
}
public class CellCons
{
 Cell cell; 
 int quantity = 1; 

 public CellCons(Cell box, int request)
 {
  //構(gòu)造函數(shù)
  cell = box; 
  quantity = request; 
 }
 public void ThreadRun( )
 {
  int valReturned;
  for(int looper=1; looper<=quantity; looper++)
  valReturned=cell.ReadFromCell( );//消費(fèi)者從操作對(duì)象中讀取信息
 }
}

然后在下面這個(gè)類MonitorSample的Main()函數(shù)中,我們要做的就是創(chuàng)建兩個(gè)線程分別作為生產(chǎn)者和消費(fèi)者,使用CellProd.ThreadRun()方法和CellCons.ThreadRun()方法對(duì)同一個(gè)Cell對(duì)象進(jìn)行操作。

public class MonitorSample
{
 public static void Main(String[] args)
 {
  int result = 0; 
 //一個(gè)標(biāo)志位,如果是0表示程序沒有出錯(cuò),如果是1表明有錯(cuò)誤發(fā)生
  Cell cell = new Cell( ); 
  //下面使用cell初始化CellProd和CellCons兩個(gè)類,生產(chǎn)和消費(fèi)次數(shù)均為20次
  CellProd prod = new CellProd(cell, 20); 
  CellCons cons = new CellCons(cell, 20); 

  Thread producer = new Thread(new ThreadStart(prod.ThreadRun));
  Thread consumer = new Thread(new ThreadStart(cons.ThreadRun));
  //生產(chǎn)者線程和消費(fèi)者線程都已經(jīng)被創(chuàng)建,但是沒有開始執(zhí)行 
  try
  {
 producer.Start( );
 consumer.Start( ); 

 producer.Join( ); 
 consumer.Join( );
 Console.ReadLine();
  }
  catch (ThreadStateException e)
  {
 //當(dāng)線程因?yàn)樗帬顟B(tài)的原因而不能執(zhí)行被請(qǐng)求的操作
 Console.WriteLine(e); 
 result = 1; 
  }
  catch (ThreadInterruptedException e)
  {
 //當(dāng)線程在等待狀態(tài)的時(shí)候中止
 Console.WriteLine(e); 
 result = 1; 
  }
  //盡管Main()函數(shù)沒有返回值,但下面這條語句可以向父進(jìn)程返回執(zhí)行結(jié)果
  Environment.ExitCode = result;
 }
}

在上面的例程中,同步是通過等待Monitor.Pulse()來完成的。首先生產(chǎn)者生產(chǎn)了一個(gè)值,而 同一時(shí)刻消費(fèi)者處于等待狀態(tài),直到收到生產(chǎn)者的“脈沖(Pulse)”通知它生產(chǎn)已經(jīng)完成,此后消費(fèi)者進(jìn)入消費(fèi)狀態(tài),而生產(chǎn)者開始等待消費(fèi)者完成操作后將 調(diào)用Monitor.Pulese()發(fā)出的“脈沖”。

它的執(zhí)行結(jié)果很簡單:

Produce: 1
Consume: 1
Produce: 2
Consume: 2
Produce: 3
Consume: 3
...
...
Produce: 20
Consume: 20
 
事實(shí)上,這個(gè)簡單的例子已經(jīng)幫助我們解決了多線程應(yīng)用程序中可能出現(xiàn)的大問題,只要領(lǐng)悟了解決線程間沖突的基本方法,很容易把它應(yīng)用到比較復(fù)雜的程序中去。

希望本文所述對(duì)大家的C#程序設(shè)計(jì)有所幫助。

相關(guān)文章

  • c# 兩個(gè)數(shù)組比較,將重復(fù)部分去掉,返回不重復(fù)部分的實(shí)現(xiàn)

    c# 兩個(gè)數(shù)組比較,將重復(fù)部分去掉,返回不重復(fù)部分的實(shí)現(xiàn)

    下面小編就為大家?guī)硪黄猚# 兩個(gè)數(shù)組比較,將重復(fù)部分去掉,返回不重復(fù)部分的實(shí)現(xiàn)方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2016-12-12
  • 詳解C#如何加密解密RAR文件

    詳解C#如何加密解密RAR文件

    這篇文章主要為大家詳細(xì)介紹了C#如何實(shí)現(xiàn)加密解密RAR文件的功能,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)C#有一定的幫助,感興趣的小伙伴可以跟隨小編一起了解一下
    2022-12-12
  • WPF中MVVM模式的理解與實(shí)現(xiàn)

    WPF中MVVM模式的理解與實(shí)現(xiàn)

    MVVM是一種設(shè)計(jì)模式,特別適用于WPF(Windows Presentation Foundation)等XAML-based的應(yīng)用程序開發(fā),MVVM模式主要包含三個(gè)部分:Model(模型)、View(視圖)和ViewModel(視圖模型),本文給大家介紹了WPF中MVVM模式的理解與實(shí)現(xiàn),需要的朋友可以參考下
    2024-05-05
  • C# 制作PictureBox圓形頭像框并從數(shù)據(jù)庫中讀取頭像

    C# 制作PictureBox圓形頭像框并從數(shù)據(jù)庫中讀取頭像

    C#提供的PictureBox控鍵默認(rèn)情況下是方形的非常大的影響美觀,怎么解決這一問題呢?下面小編給大家?guī)砹薈# 制作PictureBox圓形頭像框并從數(shù)據(jù)庫中讀取頭像的操作代碼,感興趣的朋友一起學(xué)習(xí)下吧
    2021-08-08
  • C#中的事務(wù)用法實(shí)例分析

    C#中的事務(wù)用法實(shí)例分析

    這篇文章主要介紹了C#中的事務(wù)用法,以一個(gè)簡單實(shí)例形式分析了C#創(chuàng)建及使用事物的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-09-09
  • C#實(shí)現(xiàn)Winform中打開網(wǎng)頁頁面的方法

    C#實(shí)現(xiàn)Winform中打開網(wǎng)頁頁面的方法

    這篇文章主要介紹了C#實(shí)現(xiàn)Winform中打開網(wǎng)頁頁面的方法,涉及WinForm中WebBrowser的相關(guān)使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-08-08
  • C#實(shí)現(xiàn)字體旋轉(zhuǎn)的方法

    C#實(shí)現(xiàn)字體旋轉(zhuǎn)的方法

    這篇文章主要介紹了C#實(shí)現(xiàn)字體旋轉(zhuǎn)的方法,涉及C#通過Matrix實(shí)現(xiàn)字體旋轉(zhuǎn)效果的方法,需要的朋友可以參考下
    2015-06-06
  • C#畫筆使用復(fù)合數(shù)組繪制單個(gè)矩形的方法

    C#畫筆使用復(fù)合數(shù)組繪制單個(gè)矩形的方法

    這篇文章主要介紹了C#畫筆使用復(fù)合數(shù)組繪制單個(gè)矩形的方法,涉及C#使用畫筆繪制圖形的相關(guān)技巧,需要的朋友可以參考下
    2015-06-06
  • C#在LINQ中使用GroupBy

    C#在LINQ中使用GroupBy

    這篇文章主要介紹了C#在LINQ中如何使用GroupBy,幫助大家更好的理解和學(xué)習(xí)c#,感興趣的朋友可以了解下
    2020-08-08
  • C#使用移位運(yùn)算符獲取漢字編碼的示例代碼

    C#使用移位運(yùn)算符獲取漢字編碼的示例代碼

    這篇文章主要為大家詳細(xì)介紹了C#如何使用移位運(yùn)算符獲取漢字編碼,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,需要的小伙伴可以參考下
    2024-01-01

最新評(píng)論

渑池县| 双鸭山市| 惠来县| 宣武区| 安岳县| 巧家县| 崇州市| 开平市| 洛南县| 梁河县| 长子县| 夹江县| 三河市| 天津市| 金川县| 甘南县| 平阳县| 德清县| 印江| 墨竹工卡县| 平舆县| 侯马市| 炎陵县| 芮城县| 蒲江县| 顺义区| 中西区| 太仆寺旗| 军事| 扶风县| 双柏县| 灵台县| 石渠县| 根河市| 嘉兴市| 全州县| 秭归县| 绥宁县| 介休市| 武安市| 盐亭县|