Начнем с самого начала:
Я пишу алгоритм для приложения Silverlight, который должен пройти через множество различных комбинаций высокой сложности, чтобы найти оптимальный. Чтобы дать алгоритму возможность использовать все данные ресурсы на клиенте, я решил предоставить параллельную версию.
Во-первых, я написал свой собственный класс планировщика, ориентированный на события, с дескриптором ожидания и блокирующим объектом, чтобы ограничить количество параллельных потоков и дождаться всех потоков в конце, пока я не запустил конечное CalculationCompletedEvent (кстати: Я использовал Backgroundworkers для выполнения многопоточности). Но что-то там не было потокобезопасным, и количество возвращаемых элементов в списке результатов не было постоянным. Я решил не тратить больше времени на поиск утечки после того, как коллега указал мне на Reactive Extensions (rx).
Чтобы получить представление о том, как это использовать, я объединил пример потребителя-производителя с некоторыми советами о том, как использовать rx ( example1 и example2 ).
Это прекрасно работает, но я не понимаю: почему мне нужно изменить размер браузера, чтобы обновить список и показать элементы, содержащие «_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>
Codebehind:
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
};
}
}