• <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>
    • 軟件測試技術
    • 軟件測試博客
    • 軟件測試視頻
    • 開源軟件測試技術
    • 軟件測試論壇
    • 軟件測試沙龍
    • 軟件測試資料下載
    • 軟件測試雜志
    • 軟件測試人才招聘
      暫時沒有公告

    字號: | 推薦給好友 上一篇 | 下一篇

    使用.NET Remoting實現并行計算

    發布: 2007-6-11 12:54 | 作者: 網絡轉載 | 來源: 網絡 | 查看: 152次 | 進入軟件測試論壇討論

    領測軟件測試網

    【簡介】 過去,做一個并行計算的試驗要費九牛二虎之力,今天,有了.NET Remoting,我們只需要完成非常少的編程工作,便可以跨多臺計算機輕松進行分布計算。在本文中,Eric Bergman-Terrell創建了一個名為Digits of Pi的應用程序,它使用并行的多臺計算機以不可思議的精度計算π值。他設法在12小時內完成了10,000位數的計算,卻只使用了相當少的計算資源。這比用一臺計算機單獨完成計算快了300%!

    歡迎進入.NET Remoting的奇妙世界!在這篇文章里,您將與我一起,親自動手體驗并行計算的威力。為了方便您更好地理解這篇文章,請首先按照下面的步驟作一番準備:

    1. 從附增光盤獲取示例應用程序及源代碼。
    2. 打開Everything.sln解決方案。此解決方案包含運行“Digits of Pi”應用程序所需的三個項目(Client、Server和ServerLoader)。還包含一個名為SimpleClient的項目。加載Everything.sln之后,請選擇Build(編譯)| Batch Build...(批編譯...)。單擊Select All(全部選定)按鈕,然后單擊Build(編譯)。編譯所有內容后,請在本地計算機以及您的LAN中的遠程計算機上安裝該軟件。
    3. 在本地計算機上,創建一個文件夾并將以下文件復制到其中:
      • Server\bin\Release\Plouffe_Bellard.dll
      • Client\bin\Release\DigitsOfPi.exe
    4. 在每個遠程計算機和本地計算機上,創建一個文件夾并將以下文件復制到其中:
      • Server\bin\Release\Plouffe_Bellard.dll
      • ServerLoader\bin\Release\ServerLoader.exe
      • ServerLoader\ServerLoader.exe.config
    5. 然后運行ServerLoader.exe程序。當然,運行ServerLoader和Digits of Pi程序之前,需要在每臺計算機上安裝.NET Framework。

    在所有遠程計算機和本地計算機上運行ServerLoader程序后,請運行Digits of Pi程序。單擊Configure...(配置...)(參見圖1),添加本地計算機名和遠程計算機名。如果不確定某臺計算機的名稱,請查看ServerLoader程序,它在表中顯示其計算機名。如果您很幸運地擁有一個多CPU系統,您只需為所有CPU輸入一次計算機名。只需在計算機名后鍵入@符號和一個編號。例如,如果您擁有一個名為“Brainiac”的雙CPU系統,則鍵入以下計算機名:“Brainiac@1”和“Brainiac@2”。為多CPU系統輸入多個計算機名可以確保所有計算機的CPU都用于計算π值。輸入所有計算機名后,單擊OK(確定)。

    圖1

    指定要計算的位數(參見圖2)并單擊Calculate(計算)。請從較少的位數開始,π值小數點后面的位數越多,程序所需的時間就越長。圖3顯示了Digits of Pi程序如何在本地計算機和遠程計算機中分配工作量,它使用TCP/IP端口9000發送請求并接收結果。接下來,我們將詳細探討Remoting、Plouffe_Bellard服務器對象、ServerLoader程序、SimpleClient程序和Digits of Pi程序。

    圖2
    圖3

    服務器對象

    服務器對象將計算指定的九位π值。它被命名為Plouffe_Bellard,因為它使用Fabrice Bellard的增強Simon Plouffe算法。雖然存在更快的算法,但Plouffe-Bellard算法非常簡單(少于300行源代碼),它使用少量的內存,并且由于九位數字可以單獨計算,因此更適于并行執行。Plouffe_Bellard.CalculatePiDigits方法將計算在指定位置開始的九位π值。例如,CalculatePiDigits(0)從第一位開始返回九位數字:141592653。CalculatePiDigits(9)從第十位開始返回九位數字,依此類推。

    ServerLoader

    ServerLoader程序將加載服務器對象,指定通過LAN訪問服務器對象的協議和端口,偵聽來自客戶端程序的傳入調用,處理調用并返回結果。特別值得注意的是,所有這些只需一行代碼便可完成,只需通過使用配置文件的路徑調用RemotingConfiguration.Configure方法。ServerLoader程序將加載名為ServerLoader.exe.config的配置文件(參見代碼段1)。此配置文件指定以SingleCall模式加載服務器對象,即每個傳入調用都由服務器對象的一個新實例處理。如果服務器對象以Singleton模式加載,每個傳入調用都將由同一個實例處理。類型屬性指定服務器對象的完整類型名稱(包括PB命名空間)及其程序集的名稱。objectUri屬性指定對象的統一資源標識符(URI)的端點。<channel>元素指定使用TCP協議,端口9000訪問服務器對象。代碼段1:ServerLoader.exe.config

    MILY: System">
    <configuration> 
      <system.runtime.remoting>  
        <application name = "ServerLoader">  
          <service> 
            <wellknown 
              mode="SingleCall" 
              type="PB.Plouffe_Bellard,Plouffe_Bellard"
              objectUri="Plouffe_Bellard"/> 
          </service> 
          <channels> 
            <channel ref="tcp server" port="9000"/>
          </channels> 
        </application> 
      </system.runtime.remoting>
    </configuration>

    SimpleClient

    我創建了一個名為SimpleClient的程序,以說明客戶端程序訪問遠程計算機上的服務器對象是多么容易。要運行SimpleClient,首先在遠程計算機上運行ServerLoader,然后在本地計算機上運行SimpleClient.exe程序。在Remote Machine(遠程計算機)文本框中輸入遠程計算機的名稱,然后單擊Calculate(計算)按鈕開始計算第一個九位π值。SimpleClient的CalculateButton_Click方法包含客戶端訪問遠程服務器所需的所有代碼(參見代碼段2)?梢允褂糜蛇h程計算機名、協議(TCP)和端口號(9000)組成的URL訪問遠程服務器。例如,要訪問我的“Pentium 200”計算機,則URL為“tcp://Pentium 200:9000/ServerLoader/Plouffe_Bellard”。創建URL后,將使用服務器的類型(Plouffe_Bellard)和URL調用Activator.GetObject。然后,返回的值被轉換為Plouffe_Bellard對象以備使用。調用其CalculatePiDigits方法時,請求被發送到遠程計算機上的ServerLoader。然后,服務器對象計算小數位。最后,在一個文本框中顯示返回客戶端程序的結果。代碼段2:用于訪問遠程服務器的SimpleClient代碼

    private void CalculateButton_Click(object sender,System.EventArgs e)
    {
      Cursor.Current = Cursors.WaitCursor;
      Plouffe_Bellard PiCalculator = null;
      String MachineName = RemoteMachineTextBox.Text;
      try
      {
        int port = 9000;
        String URL = "tcp://" + MachineName + ":" + 
           port + "/ServerLoader/Plouffe_Bellard";
        PiCalculator = (Plouffe_Bellard) 
           Activator.GetObject(typeof(Plouffe_Bellard), URL);
        ResultsTextBox.Text = "3." + 
           PiCalculator.CalculatePiDigits(1);
      }
      catch(Exception)
      {
        MessageBox.Show(
           "需要在計算機" +
           MachineName,
           "Simple Client上運行ServerLoader.exe", 
           MessageBoxButtons.OK,
           MessageBoxIcon.Error);
      }
      Cursor.Current = Cursors.Arrow;
    }

    Digits of Pi客戶端

    Digits of Pi客戶端程序比SimpleClient更復雜。SimpleClient僅通過訪問遠程計算機上的服務器對象來計算前九位π值。而Digits of Pi則同時使用Configure(配置)對話框中指定的遠程計算機和本地計算機(如圖1所示)并行計算用戶指定的小數位。服務器對象在單獨的線程中訪問,以便在可能需要很長時間的計算過程中保持Digits of Pi GUI對用戶操作的響應性。

    Digits of Pi使用數組將作業分為九位數據塊,將工作量分配到所有可用的計算機上。用戶單擊Calculate(計算)按鈕后,將創建SolutionArray(參見圖4)。SolutionArray為要計算的每組九位π值分配一個SolutionItem元素。服務器對象計算m_Digit字段指定的九位數組后,數位將存儲在m_Results成員中。m_MachineName成員包含運行服務器的計算機的名稱。存儲計算機名是為了使Digits of Pi能夠顯示每臺計算機計算的小數總數(參見圖2)。

    圖4

    為使服務器對象并行計算,Digits of Pi將為每個服務器對象創建一個線程并啟動線程計算。然后,必須等待所有線程完成計算后才能顯示最終結果。WaitHandle對于等待多個線程很有用。Digits of Pi將為每個線程使用一個WaitHandle,以等待所有線程完成計算。

    將調用CalculationThread.Calculate(參見代碼段3)以便為每個服務器對象創建一個線程。該操作將啟動線程運行,然后返回一個AutoResetEvent(從WaitHandle衍生而來)。每個線程的AutoResetEvent都存儲在一個數組中,然后數組被傳遞給WaitHandle.WaitAll。完成線程計算后,將對其AutoResetEvent調用Set方法。最后一個線程調用Set方法后,將返回WaitAll調用,并顯示π的值。代碼段3:CalculationThread。

    public static WaitHandle Calculate(
    SolutionArray solutionArray, String machineName)
    {
      CalculationThread calculationThread = new 
        CalculationThread(solutionArray, machineName);
      Thread thread = new Thread(new 
        ThreadStart(calculationThread.Calculate));
      thread.Start();
      return calculationThread.calculationDone;
    }

    每個線程都使用相同的算法:如果有更多的工作要處理,線程將奪取下一個SolutionItem,在SolutionItem中存儲服務器對象的計算機名,計算指定的九位小數,并將結果存儲在SolutionItem中。此進程將一直運行,直到所有SolutionItem中都填充了結果。有關詳細信息,請參見代碼段4。代碼段4:CalculationThread.Calculate

    public void Calculate()
    {
      Plouffe_Bellard PiCalculator = 
        RemotePiCalculator.GetPiCalculator(
          GetRealMachineName(machineName));
      if (PiCalculator != null)
      {
        SolutionItem Item = null;
        bool Abort;
        do
        {
          Abort = solutionArray.Abort;
          if (!Abort)
          {
            Item = solutionArray.GetNextItem();
            if (Item != null)
            {
              Item.MachineName = machineName;
              try
              {
                Item.Results = 
                  PiCalculator.CalculatePiDigits(Item.Digit);
              }
              catch (Exception e)
              {
                Abort = true;
                MessageBox.Show(
                  "無法訪問主機上的遠程對象" +
                  machineName +
                  Environment.NewLine + 
                  Environment.NewLine +
                  "Message:  " + 
                  e.Message, Globals.ProgramName, 
                  MessageBoxButtons.OK, 
                  MessageBoxIcon.Error);
              }
              UpdateStatisticsDelegate USD = new 
                UpdateStatisticsDelegate(
                  MF.UpdateStatistics);
              MF.Invoke(USD, new Object[] {} );
            }
          }
        } while (Item != null && !Abort);
        calculationDone.Set();
      }
    }

    下面是每一步的說明:

    1. GetRealMachineName從多CPU計算機名中刪除@1模式。例如,GetRealMachineName("Brainiac@1")返回“Brainiac”。有關多CPU計算機名的解釋,請參見圖1對話框中的文本。
    2. 知道正確的計算機名后,將其傳遞給RemotePiCalculator.GetPiCalculator,這樣才可以通過PiCalculator變量訪問該計算機上的服務器對象。
    3. 如果用戶單擊了Cancel(取消)按鈕,將設置Abort屬性。如果Abort屬性為true,線程將停止計算。
    4. 對MF.Invoke的調用使線程可以安全地更新ListView中的統計數據(參見圖2),即使該ListView是由另一個線程創建的。在32位Windows編程中,絕不允許在創建某個控件的線程之外處理該控件。
    5. 完成循環(即計算完指定的所有π位數或者用戶單擊Cancel [取消]按鈕)后,將調用線程的AutoResetEvent的Set函數。
    6. 當每個線程都調用其AutoResetEvent的Set函數后,將返回對WaitHandle.WaitAll的調用并顯示結果。

    線程同步

    如果Digits of Pi的代碼由多個線程同時訪問,可能會有多個地方出現錯誤。例如,如果兩個線程同時調用SolutionArray.GetNextItem,可能會返回相同的內容。這就是在GetNextItem方法中設置[MethodImpl(MethodImplOptions.Synchronized)]屬性的原因,該屬性可以確保一次只有一個線程調用該方法。如果方法的每一行代碼都不應由多個線程同時訪問,則使方法同步是一個很好的策略。

    由于MainForm.Calculate方法只有一行代碼不能同時被多個線程訪問,因此它將在該行代碼之前調用Monitor.Enter,并在其后調用Monitor.Exit。如果該行代碼已在其他線程上運行,Monitor.Enter將被阻止。如果整個函數已實現同步,那么只保護需要防止多個線程訪問的代碼行就可以提高性能。

    從System.Windows.Forms.Control派生的對象(例如Button、TextBox、RichTextBox、Label、ListBox、ListView等等)只應由創建它們的線程處理。要從非創建線程中安全處理Control衍生對象,請首先將處理代碼放入一個方法,然后為該方法聲明一個代理:

    delegate void SetResultsTextDelegate(String Text);
    
    private void SetResultsText(String Text)
    {
      ResultsRichTextBox.Text = Text;
    }

    然后使用Form.Invoke間接調用該方法:

    SetResultsTextDelegate SRTD = new 
       SetResultsTextDelegate(SetResultsText);
    
    Invoke(SRTD, new object[] { "" } );

    Invoke方法將從創建它的線程中調用該方法,它使用的參數與對象數組中的元素相對應。

    小結

    .NET Remoting是一種在遠程(和本地)計算機上執行代碼簡單有效的機制。只需將代碼封裝到.NET對象中,編寫加載該對象并偵聽請求的程序,然后在客戶端程序中調用Activator.GetObject。如果您的LAN中有一些閑置的計算機,可以利用它們輕松地解決并行問題。只需記住要使用正確的線程同步機制,以防止線程之間發生沖突。

    延伸閱讀

    文章來源于領測軟件測試網 http://www.kjueaiud.com/

    TAG: net remoting 實現 使用 并行 計算


    關于領測軟件測試網 | 領測軟件測試網合作伙伴 | 廣告服務 | 投稿指南 | 聯系我們 | 網站地圖 | 友情鏈接
    版權所有(C) 2003-2010 TestAge(領測軟件測試網)|領測國際科技(北京)有限公司|軟件測試工程師培訓網 All Rights Reserved
    北京市海淀區中關村南大街9號北京理工科技大廈1402室 京ICP備10010545號-5
    技術支持和業務聯系:info@testage.com.cn 電話:010-51297073

    軟件測試 | 領測國際ISTQBISTQB官網TMMiTMMi認證國際軟件測試工程師認證領測軟件測試網

    老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>