1

讓我們從頭開始:Silverlight和RX:爲什麼我必須調整瀏覽器來更新UI?

我正在爲Silverlight應用程序編寫一個算法,該算法必須通過高複雜度的許多不同組合來找到最佳值。爲了給算法提供使用客戶端上所有給定資源的可能性,我決定提供一個並行版本。

首先,我編寫了自己的異步事件驅動的調度器類,它帶有一個等待句柄和一個阻塞對象,用於限制並行線程的數量並等待最後的所有線程,直到我終止了CalculationCompletedEvent :我使用Backgroundworkers來執行多線程)。但是有些東西沒有線程安全,並且結果列表中返回元素的數量並不是恆定的。在一位同事指出我關於Reactive Extensions(rx)後,我認爲不要花費更多時間尋找泄漏。

想要知道如何使用這個,我將consumer-producer example與一些關於如何使用rx(example1example2)的建議結合在一起。

這很好,但我不明白的是:爲什麼我必須調整瀏覽器的大小以更新列表框並顯示包含「_receivedStrings」元素?再次是一個小小的愚蠢的忽視?順便說一句:如果你不會推薦使用rx,請給我一個鏡頭,告訴我爲什麼以及使用其他方法。

XAML:

<UserControl x:Class="ReactiveTest.MainPage" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    mc:Ignorable="d" 
    d:DesignHeight="300" d:DesignWidth="400"> 

    <Grid x:Name="LayoutRoot" Background="White"> 
     <Grid.RowDefinitions> 
      <RowDefinition/> 
      <RowDefinition Height="Auto"/> 
     </Grid.RowDefinitions> 
     <ListBox HorizontalAlignment="Stretch" Name="listBox1" 
       VerticalAlignment="Stretch" ItemsSource="{Binding}"/> 
     <Button Grid.Row="1" Content="Klick me!" Width="Auto" Height="Auto" 
       HorizontalAlignment="Center" Click="Button_Click"/> 
    </Grid> 
</UserControl> 

代碼隱藏:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Net; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Documents; 
using System.Windows.Input; 
using System.Windows.Media; 
using System.Windows.Media.Animation; 
using System.Windows.Shapes; 
using System.IO; 
using System.Reactive.Linq; 

namespace ReactiveTest 
{ 
    public partial class MainPage : UserControl 
    { 
     private int _parallelThreadsAmount; 

     public IList<String> receivedStrings; 

     public MainPage() 
     { 
      InitializeComponent(); 
      receivedStrings = 
       new List<String>(); 
      this._parallelThreadsAmount = 10; 

      this.listBox1.DataContext = receivedStrings; 
     } 

     private void Button_Click(object sender, RoutedEventArgs e) 
     { 
      IList<IObservable<String>> obsCollection = 
       new List<IObservable<String>>(); 

      foreach (var item in forums) 
      { 
       obsCollection.Add(Calculate(item)); 
      } 
      DateTime start = DateTime.Now; 

      obsCollection.Merge(this._parallelThreadsAmount) 
       .Subscribe(
        y => 
        { 
         receivedStrings.Add(
          String.Format("{0} - Received: {1}", receivedStrings.Count, y)); 
        }, 
        () => 
        { 
         DateTime end = DateTime.Now; 
         TimeSpan elapsed = end - start; 
         this.receivedStrings.Add(
          String.Format(
           "{0}/{1} done in {2} ms.", 
           receivedStrings.Count, 
           forums.Count(), 
           elapsed.TotalSeconds) 
          ); 
        } 
       ); 
     } 

     IObservable<String> Calculate(String source) 
     { 
      Random rand = new Random(); 
      return Observable.Defer(() => Observable.Start(() => 
      { 
       // simulate some work, taking different time, 
       // to get the threads end in an other order than they've been started     
       System.Threading.Thread.Sleep(rand.Next(500, 2000)); 
       return source; 
      })); 
     } 


     static readonly String[] forums = new string[] 
     { 
      "announce", 
      "whatforum", 
      "reportabug", 
      "suggest", 
      "Offtopic", 
      "msdnsandbox", 
      "netfxsetup", 
      "netfxbcl", 
      "wpf", 
      "regexp", 
      "msbuild", 
      "netfxjscript", 
      "clr", 
      "netfxtoolsdev", 
      "asmxandxml", 
      "netfx64bit", 
      "netfxremoting", 
      "netfxnetcom", 
      "MEFramework", 
      "ncl", 
      "wcf", 
      "Geneva", 
      "MSWinWebChart", 
      "dublin", 
      "oslo", 
      // … some more elements 
     }; 
    } 
} 

回答

1

Ignoreing缺乏MVVM,IoC的,可測試性等...... 你不執行INotifyPropertyChanged,你不使用的ObservableCollection(T)的。 1.更改您的公共領域的pubilc只讀屬性 2.使用的ObservableCollection,而不是IList的

//public IList<String> receivedStrings; 
private readonly ObservableCollection<string> _receivedStrings = new ObservableCollection<string>(); 
public ObservableCollection<string> ReceivedStrings 
{ 
    get { return _receivedStrings;} 
} 

,你可能還需要使用ObserveOnDispatcher(),以確保您叫回調度程序,因爲你不能在Dispatcher的線程中更新UI(即使通過綁定)。

obsCollection.Merge(this._parallelThreadsAmount)     
    .ObserveOn(Scheduler.Dispatcher) 
    //-or-.ObserveOnDispatcher() 
    //-or even better -.ObserveOn(_schedulerProvider.Dispatcher) 
    .Subscribe(
+0

謝謝李。我顯然是這個行業的新人,到現在爲止我沒有使用UI。但我試圖一步一步來MVVM。你幫助我再接近一小步。這應該是足夠的今天,因爲它只是另外:) – germanSharper 2012-02-13 13:00:08

1

不知怎的,我很想說,你是一個有點糊塗,其中Rx,這是很正常的,說實話:)

李坎貝爾寫有對於初學者的幾件事情,調整大小問題與Rx無關,因爲我看到它,但它是ObservableCollection的東西。

除此之外,我已經修改了一些代碼來處理一些我所看到的內容,但我不確定它是否正是你想要的,因爲你有特定的平行想要的東西,我會大膽的說,這不是你的擔憂(並希望我在這裏不是一個聰明人)。

private void Button_Click(object sender, RoutedEventArgs e) 
{ 
    //extension method in Rx for wrapping 
    var obsCollection = forums.ToObservable(); 

    //Observing on the dispatcher to prevent x-thread exceptions 
    obsCollection.Select(Calculate).ObserverOnDispatcher().Subscribe(
     receivedString => { 
       receivedStrings.Add(String.Format("{0} - Received: {1}", receivedStrings.Count, y)); 
      }, 
      ()=>{ 
       DateTime end = DateTime.Now; 
       TimeSpan elapsed = end - start; 
       this.receivedStrings.Add(
        String.Format("{0}/{1} done in {2} ms.", receivedStrings.Count, forums.Count(), elapsed.TotalSeconds) 
       ); 
      } 
    ); 
} 

Random rand = new Random(); 
IObservable<string> Calculate(string inputString) 
{ 
    //launching the "calculation" on the taskpool...can be changed to other schedulers 
    return Observable.Start(
     ()=>{ 
      Thread.Sleep(rand.Next(150,250)); 

      return inputString; 
      }, Scheduler.ThreadPool 
     ); 
} 
+0

感謝cyberzed,這是真的intersting和教育。我將在未來的實踐中嘗試使用這種方法。關於「多少個並行線程」的觀點你是可行的。我從我的第一個實現中移植出來,在那裏我開始了一些背景工作,阻止了生產者,直到所有人都完成了,檢查了一些東西,然後決定是否取消,因爲我已經有了,我正在尋找什麼或者開始另一個背景工作。但是在這種情況下,從數組中獲取給定數量的元素並不關心合併會更好,對吧? – germanSharper 2012-02-13 13:18:32

+1

我看了一下,在想要控制並行量的情況下,您可能想要將它與TPL結合起來(不記得它可用的SL版本) - http:// social.msdn.microsoft.com/Forums/en-US/rx/thread/12d3f79a-0a53-4854-976e-5fa0d86f01ee – cyberzed 2012-02-13 13:39:17

+0

你是對的。現在我正在使用TPL。使用任務似乎更好,因爲如果線程需要或不需要,讓操作系統(或任何曾經)選擇。它很容易用來同步每個x次。幫助了我很多:)再次感謝 – germanSharper 2012-02-22 11:07:01

相關問題