- Zaczynamy [0,5 pkt]
- Utworzyć nową solucję a w niej projekt typu konsolowego .net core
- Zademonstrować działanie poniższej funkcji:
private static Task<int[]> calculateSomethingAsync(int size)
{
return Task.Factory.StartNew<int[]>((s) => {
if (s==null) { throw new ArgumentNullException(nameof(s)); }
int[] result = new int[(int)s];
for (int i = 0; i < (int)s; i++)
{
Thread.SpinWait(30000000);
result[i] = Random.Shared.Next(10);
Console.WriteLine($"I calculated element {i} as [{result[i]}]");
}
return result;
},size);
}
- Raportowanie postępu [0,5 pkt.]
- Przeciążyć powyższą funkcję tak by zamiast napisu "I calculated element..." raportowała za pomocą interfejsu
IProgress<(int,int)>
.
- Uwzględnić możliwość podania null zamiast Progress.
- Zademonstrować działanie dodając obsługę eventu
ProgressChanged
wypisując numer iteracji i wylosowaną wartość .
- Anulowanie [0,5 pkt.]
- Do powyższej metody dodać możliwość anulowania za pomocą
CancellationToken
- Metoda powinna sprawdzać przy każdej iteracji czy nastąpiło żądanie anulowania i rzucić wyjątkiem wraz z odpowiednim komunikatem, np. "Nastąpiło żądanie anulowania".
- Uruchomić aplikację i przetestować anulowanie używając
CancelAfter(5000)
(z obiekt klasy CancellationTokenSource
). Dzięki temu zażądamy anulowania po upływie 5 sekund.
- W Main przechwycić wygenerowany wyjątek. Można użyć AggregateException.Flatten().
- Użycie continueWith opcjonalne [0,5 pkt.]
- na utworzonym tasku z metody
calculateSomethingAsync
wykonać ContinueWith
w przypadku błędu (TaskContinuationOptions.NotOnRanToCompletion
). Utworzy nam się alternatywny task, nazwijmy go onFaultTask.
- Wypisać błąd przekazany od poprzednika
- podobnie przy pomocy
ContinueWith
stworzyć task wyświetlający zawartość zwróconej tablicy ale tylko w przypadku poprawnego zakończenia (TaskContinuationOptions.OnlyOnRanToCompletion
)
- Poczekać na oba taski za pomocą
Task.WaitAll
. Tu wymagane utworzenie tablicy obu tasków.
- Przechwycić wyjątek rzucony przez task alternatywny.
- Przetestować przez zakomentowanie/odkomentowanie
CancelAfter(5000)
- Wykorzystanie ParallelFor [0,5 pkt]
- Na podstawie powyższego przeprowadź eksperymenty z sumowaniem równoległym za pomocą [1 pkt.]
Parallel.ForEach(numbers, number => sum += number);
Zmierz czas i sprawdź czy suma jest identyczna
- Zabezpiecz sumowanie za pomocą lock. Zmierz czas, sprawdź poprawność wyników.
- Wykonaj sumowanie za pomocą jednowątkowej metody Linq Sum()
numbers.Sum(number => number);
Zmierz czas, sprawdź poprawność wyników.
- Wykonaj to samo dla zapytania PLINQ
numbers.AsParallel().ForAll(number => sum += number);
Zmierz czas, sprawdź poprawność wyników.
- Zabezpiecz powyższe wyliczenia za pomocą lock. Zmierz czas, sprawdź poprawność wyników.
- Wykonaj sumowanie za pomocą sumy z PLINQ na dwa sposoby:
numbers.AsParallel().Sum(number => number);
numbers.AsParallel().Sum();
- Zmierz czas i sprawdź poprawność wyników. Jednak zrób to uruchamiając program najpierw z pierwszym przypadkiem potem z drugim a następnie z obydwoma. Porównaj wyniki.
- Wykonaj sumowanie na zwykłych taskach [0,5 pkt.]
- Do odczytania liczby procesorów użyj
Environment.ProcessorCount
- Poczekaj na zakończenie tasków i zsumuj od nich wyniki.
double[] partialResults = Task.WhenAll(tasks).Result;
sum = partialResults.Sum();
- Oczywiście wykonaj pomiary czasu i zweryfikuj poprawność danych.
- Sumowanie z obliczaniem [1 pkt.]
- Wykonaj wszystkie testy dla przykładu z punktu 6 oraz 7, ale zsumuj wyniki funkcji
static double calculatedValue(double value)
{
return (Math.Sin(Math.Sin(value * value) * Math.Cos(value * value) / Math.PI) * Math.Sin(Math.Sin(value * value) * Math.Cos(value * value) / Math.PI)) > 1 ? 0 : 1; //finally it always returns 1 to simplify checking
}
- Np. Dla operacji jednowątkowej:
foreach (int number in numbers)
{
sum += calculatedValue(number);
}