熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> ASP編程 >> 正文

.NET 4並行編程之共享數據問題和解決概述

2013-11-15 12:51:29  來源: ASP編程 

  在開始之前首先我們來看一個很有趣的例子

  class BankAccount

  {

  public int Balance

  {

  get;

  set;

  }

  }

  class App

  {

  static void Main(string[] args)

  {

  // create the bank account instance

  BankAccount account = new BankAccount();

  // create an array of tasks

  Task[] tasks = new Task[];

  for (int i = ; i < ; i++)

  {

  // create a new task

  tasks[i] = new Task(() =>

  {

  // enter a loop for balance updates

  for (int j = ; j < ; j++)

  {

  // update the balance

  accountBalance = accountBalance + ;

  }

  });

  // start the new task

  tasks[i]Start();

  }

  // wait for all of the tasks to complete

  TaskWaitAll(tasks);

  // write out the counter value

  ConsoleWriteLine(Expected value {} Counter value: {}

   accountBalance);

  // wait for input before exiting

  ConsoleWriteLine(Press enter to finish);

  ConsoleReadLine();

  }

  }

  個task每個task都是把BankAccountBalance自增之後代碼就等到個task執行完畢然後打印出Balance的值大家猜想一下上次的代碼執行完成之後打印出來的Balance的結果是多少?

  J結果確實和大家猜想的一樣結果不等於每次執行一次上面的代碼都會得到不同的結果而且這些結果值都在左右如果運氣好可能看到有那麼一兩次結果為為什麼會這樣?

  下面就是本篇和接下來的幾篇文章要講述的內容

  數據競爭

  如果大家對多線程編程比較熟悉就知道上面情況的產生是因為 共享數據競爭導致的(對多線程不熟悉不清楚的朋友也不用擔心)當有兩個或者更多的task在運行並且操作同一個共享公共數據的時候就存在潛在的競爭如果不合理的處理競爭問題就會出現上面意想不到的情況

  下面就來分析一下上面代碼的情況是怎麼產生的

  當在把account對象的Balance進行自增的時候一般執行下面的三個步驟

  讀取現在account對象的Balance屬性的值

  計算創建一個臨時的新變量並且把Balance屬性的值賦值給新的變量而且把新變量的值增加

  把新變量的值再次賦給account的Balance屬性

  在理論上面上面的三個步驟是代碼的執行步驟但是實際中由於編譯器NET 運行時對自增操作的優化操作和操作系統等的因素在執行上面代碼的時候並不一定是按照我們設想的那樣運行的但是為了分析的方便我們還是假設代碼是按照上面的三個步驟運行的

  之前的代碼每次執行一次執行代碼的計算機就每次處於不同的狀態CPU的忙碌狀況不同內存的剩余多少不同等等所以每次代碼的運行計算機不可能處於完全一樣的環境中

  在下面的圖中顯示了兩個task之間是如何發生競爭的當兩個task啟動了之後(雖然說是並行運算但是不管這樣兩個的task的執行時間不可能完全一樣也就是說不可能恰好就是同時開始執行的起碼在開始執行的時間上是有一點點的差異的)

  .    首先Task讀取到當前的balance的值為

  .    然後task運行了並且也讀取到當前的balance值為

  .    兩個task都把balance的值加

  .    Task把balance的值加把新的值保存到了balance中

  .    Task 也把新的保存到了balance中

  所以結果就是雖然兩個task 都為balance加但是balance的值還是

  通過這個例子相信大家應該清楚為什麼上面的個task執行而執行後的結果不是

  .  解決方案提出

  數據競爭就好比一個生日party其中每一個task都是參加party的人當生日蛋糕出來之後每個人都興奮了如果此時所有的人都一起沖過去拿屬於他們自己的那塊蛋糕此時party就一團糟了沒有如何順序

  在之前的圖示例講解中balance那個屬性就好比蛋糕因為tasktask都要得到它然後進行運算當我們來讓多個task共享一個數據時就可能出現問題下面列出了四種解決方案

  .    順序執行也就是讓第一個task執行完成之後再執行第二個

  .    數據不變我們讓task不能修改數據

  .    隔離我們不共享數據讓每個task都有一份自己的數據拷貝

  .    同步通過調整task的執行有序的執行task

  注意同步和以前多線程中的同步或者數據庫操作時的同步概念不一樣

  順序的執行的解決方案

  順序的執行解決了通過每次只有一個task訪問共享數據的方式解決了數據競爭的問題其實在本質上這種解決方案又回到了之前的單線程編程模型如果拿之前的party分蛋糕的例子那麼現在就是一次只能允許一個人去拿蛋糕

  數據不變解決方案

  數據不變的解決方案就是通過讓數據不能被修改的方式來解決共享數據競爭如果拿之前的蛋糕為例子那麼此時的情況就是現在蛋糕只能看不能吃

  在C#中可以同關鍵字 readonly 和 const來聲明一個字段不能被修改

  public const int AccountNumber=;

  被聲明為const的字段只能通過類型來訪問上面的AccountNumber是在Blank類中聲明的那麼訪問的方式就是Blank AccountNumber

  readonly的字段可以在實例的構造函數中修改

  如下代碼

  using System;

  class ImmutableBankAccount

  {

  public const int AccountNumber = ;

  public readonly int Balance;

  public ImmutableBankAccount(int InitialBalance)

  {

  Balance = InitialBalance;

  }

  public ImmutableBankAccount()

  {          Balance = ;

  }

  }

  class App

  {

  static void Main(string[] args)

  {

  // create a bank account with the default balance

  ImmutableBankAccount bankAccount = new ImmutableBankAccount();

  ConsoleWriteLine(Account Number: {} Account Balance: {}

  ImmutableBankAccountAccountNumber bankAccountBalance);

  // create a bank account with a starting balance

  ImmutableBankAccount bankAccount = new ImmutableBankAccount();

  ConsoleWriteLine(Account Number: {} Account Balance: {}

  ImmutableBankAccountAccountNumber bankAccountBalance);

  // wait for input before exiting

  ConsoleWriteLine(Press enter to finish);

  ConsoleReadLine();

  }

  } 數據不變的解決方案不是很常用因為它對數據限制太大了


From:http://tw.wingwit.com/Article/program/ASP/201311/21756.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.