piątek, marzec 26, 2010

WPF – Ku pamięci

Ponownie nie jest mi dane pisać o TPL. Cóż ważne, że dane jest pisać o czymkolwiek technicznym. Dziś będzie o WPFie i dziwnym błędzie, który męczył mnie przez prawie dwa dni. Być może oszczędzi to komuś trudów. Zacznijmy zatem…

Aplikacją oczywiście w WPFie. Jako, że budowałem dla niej instalator postanowiłem sprawdzić czy wszystkie niezbędne komponenty są dołączane. Chciałem oczywiście, aby w przypadku braku .NET instalował się on skorzystałem z rozwiązania podanego przez Bartka Szafko we wpisie Instalowanie komponentów zależnych. Mój bootstraper wyglądał tak:

<ItemGroup>
    <BootstrapperFile Include="Microsoft.Net.Framework.3.5">
      <Visible>False</Visible>
      <ProductName>.NET Framework 3.5</ProductName>
      <Install>true</Install>
    </BootstrapperFile>
    <BootstrapperFile Include="Microsoft.Windows.Installer.3.1">
      <ProductName>Windows Installer 3.1</ProductName>
    </BootstrapperFile>
</ItemGroup>

Pomijając nieistotne elementy…

Instalator się ładnie budował, proces instalacji na czystej wirtualce XP przebiegał bez problemu ale po zainstalowaniu zamiast działającej aplikacji raczył mnie takim o to widokiem:

error

Ciutkę tajemniczę. Kernel32.dll? Hmm…problem potęgował fakt, że inna aplikacja w WPF działała bez najmniejszego zająknięcia. Próbowałem już chyba wszystkiego: wyrzucanie dll’ek (Enterprise Library, SharpZipLib, HtmlAgilityPack), które mogły powodować, że nie mam czegoś zainstalowanego na tej maszynie. Podglądanie pod reflektorem zależności i próba wymyślenia czego może tam brakować. Instalowanie multum pakietów z nadzieją, że może to rozwiąże problem. Nic nie pomagało…

Byłem praktycznie pokonany, ale dziś czytając o wydajności aplikacji w WPF i jak ją możemy zwięksyć natknąłem się na zadanie, iż w 3.5 SP1 dodano do WPF sporo usprawnień. Żółta lampka :). Wiedziałem, że SP1 dodaje trochę nowych rzeczy do WPFa i pomyślałem o tym, jakby się taki błąd gdy użyję czegoś z SP1 a mam zainstalowany tylko .NET 3.5 objawiał. Sprawdziłem i oczywiście mój bootstraper instalował tylko .NET 3.5 bez SP1. Szybko pobrałem SP1 i zainstalowałem, aby sprawdzić….no i udało się. Program wystartował. Eureka.

Oczywiście problemem był ContentStringFormat, który został dodany w SP1 (lub SP3 do .NET 3.0). W WiX’ie oczywiście miałem sprawdzanie zainstalowania tylko .NET 3.5 dlatego instalator działał poprawnie…

Głupi błąd, ale może ten wpis zaoszczędzi komuś cenny czas. Błąd ten jednak powinien być trochę mniej tajemniczy… teraz zostało tylko dołączyć .NET 3.5 SP1 jako Prerequsite z czym też jest trochę problemów, ale przynajmniej wiadomo co robić.

wtorek, marzec 09, 2010

TPL - Parallel.For/ForEach

Wracamy do kursu o TPL. Dziś trochę informacji faktów o .For oraz .ForEach.
Te dwie metody dają podstawowy sposób na zrównoleglenie naszego kodu bez większego wysiłku

Parallel.For(lowerBound, exclusiveUpperBound, () => { /*action*/});

Parallel.ForEach<int>(Enumerable.Range(1, 100), (int item) => { /*body*/});

Tak więc prościej już by się nie dało :). Jednak funkcje te mają możliwość przyjmowania dodatkowych parametrów.

ParallelOptions

Za pomocą tego parametru możemy ustawić kilka dodatkowych właściwości takich jak:


  • maksymalny stopień paralelizacji (MaxDegreeOfParallelism)
  • token anulacji (CancellationToken)
  • własny scheduler zadań (TaskScheduler)

Zakończenie pętli

Sytuacja, gdy pętla zakończy się powodzeniem jest jak najbardziej przez nas pożądana, ale oczywiście trzeba przygotować się na sytuację, gdy coś się nie uda lub pętla zakończy się przedwcześnie. Aby sprawdzić, jak zakończyła się pętla należy sprawdzić zawartość zmiennej zwracanej przez Parallel.For – zwracany jest obiekt ParallelLoopResult.

Stop
Parallel.For(1, 1000, (int i, ParallelLoopState state) =>
{
/*action*/
if (found)
{
state.Stop();
}
});

Warto zauważyć inną definicję ciała pętli. Jeśli chcemy skorzystać z możliwości zakończenia pętli przedwcześnie musimy wykorzystać definicję przyjmującą jako argumenty indeks oraz typ ParallelLoopState. Po wywołaniu metody Stop, kolejne iteracje nie będą uruchamiane. Jeśli chcemy przerwać już uruchomione iteracje możemy sprawdzić właściwość IsStopped i odpowiednio na nią reagować.

Break

Break działa podobnie do Stop ale daje dodatkowe gwarancje. Jeśli Break zostanie wywołane z 50 iteracji, spowoduje zatrzymanie wywoływania iteracji powyżej 50-tej, ale iteracje 0-49 nadal będą wywołane. Break może zostać wywołane wielokrotnie a najniższa iteracja, z której została wywołana metoda zostaje zapisana we właściwości: LowestBreakIteration.

Stop i Break nie mogą być używane równocześnie.

Wyjątki

Czasem mamy sytuację, iż nasze ciało pętli rzuca wyjątkiem. Jak sobie radzić w takiej sytuacji. TPL wprowadza nowy typ wyjątku: AggregateException. Nie różni się to od zwykłego wyjątku, z tym, że umożliwia zapisanie wiele wyjątków w polu InnerException. Aby sprawdzić czy w jakiejś iteracji nie wystąpił wyjątek należy sprawdzić właściwość IsExceptional na obiekcie ParallelLoopState.


Na dziś to tyle. Zachęcam do zabawy TPL w .NET 4.0 naprawdę przydatna sprawa.

środa, marzec 03, 2010

Rozważania nad Binding.StringFormat

Dzisiejszy wpis bardziej dla mnie na przyszłość ale może ktoś skorzysta.

Ostatnio miałem potrzebę użycia takiej wartości StringFormat, aby jego wartość zaczynała się od spacji. Każdy kto kiedykolwiek bawił się tą właściwością wie, że takie coś jak poniżej nie spełni swojej roli.

<TextBlock FontSize="16" Text="{Binding Value, Mode=OneWay, StringFormat= ({0}) dB}" x:Name="test" />

Zauważcie spację pomiędzy znakiem = a ( w parametrze StringFormat. Wiem, że przykład może naciągany, ale w moim przypadku taki coś było konieczne.

Wynik jest jednak taki jak poniżej.

image

Jak zatem możemy uzyskać prowadzącą spację (leading space po polsku)? StringFormat obsługuje specjalną parę znaków {}. Wystarczy nasz StringFormat rozpocząć od tych znaków a nasza spacja będzie uwzględniona

<TextBlock FontSize="16" Text="{Binding Value, Mode=OneWay, StringFormat={} ({0}) dB}" x:Name="test" />

image

Tę samą sztuczkę musimy zastosować jeśli nasz StringFormat chcemy rozpocząć znakiem {.

W dokumentacji MSDN znak ten pojawia się w przykładzie, ale nie ma nic na jego temat – BindingBase.StringFormat property.