Pozwolę sobie napisać jeszcze jednego posta, bo mam większą chwilę żeby rozwinąć temat.
Początkujący programiści często mają problem ze zrozumieniem asynchroniczności, mylą ją z równolegleniem zadań. Jakiś czas temu jak sam się tego uczyłem wymyśliłem analogię z systemem obsługi w popularnej sieci restauracji. Kiedyś w McDonaldach na jedzenie czekało się synchronicznie, obecnie po wprowadzeniu systemu kolejkowania odbywa się to asynchronicznie. Na potrzeby wyjaśnienia nagnę trochę zasady.
Synchroniczność vs asynchroniczność vs równoległość
Synchroniczny nierównoległy McDonalds: Jest jeden kasjer, jeden kucharz.
- zamawiamy burgera (czekamy przy kasie aż dostaniemy jedzenie),
- kasjer przekazuje zamówienie do kucharza,
- kucharz smaży burgera,
- dostajemy zamówienie
var burger = CookMakeMeBurger();
Eat(burger);
Sychroniczny równoległy McDonalds: n kasjerów, n kucharzy
Tak samo jak w nierównoległym, tylko są na przykład 4 koljeki, których kasjerzy mogą odbierać zamówienia niezależnie. Co ważne w tym podejściu kasjer ma przypisanego swojego kucharza i nie może dać zamówienia innemu.
// never do this, do not use THREADS!
// for demonstration purposes only
var burgers = new Burger[3];
var burgerThreads = Enumerable
.Range(0, 3)
.Select(i => new Thread(
() => burgers[i] = CookMakeMeBurger()))
.ToArray();
foreach (var t in burgerThreads)
{
t.Join();
}
foreach (var burger in burgers)
{
Eat(burger);
}
Nigdy nie powinno się używać wątków wprost. Ktoś kiedyś w Microsofcie słusznie zauważył, że utworzenie i uruchomienie wątku jest kosztowne obliczeniowo i dlatego powinno się używać klasy Task, która korzysta z tzw. puli wątków.
Asynchroniczny nierównoległy McDonalds: n kasjerów, 1 kucharz
- zamawiamy burgera,
- dostajemy od kasjera numerek zamówienia,
- możemy robić coś innego, np. iść do ubikacji,
- kiedy skończymy robić [coś innego], a zamówienie jest już gotowe to odbieramy, jeśli nie jest to czekamy na burgera już synchronicznie.
var burgerTask = Task.Run(() =>
{
return CookMakeMeBurger();
});
GotoToilet();
var burger = await burgerTask;
Eat(burger);
Asynchroniczny równoległy McDonalds: n kasjerów, n kucharzy
Podobnie jak w poprzednim przypadku, jednak mamy n kucharzy, czyli jakby kilka wątków. System kolejkowania zarządza, który kucharz dostanie które zamówienie.
var burgerTasks = Enumerable
.Repeat(1, 3)
.Select(_ => Task.Run(() => CookMakeMeBurger()))
.ToArray();
GotoToilet();
var burgers = await Task.WhenAll(burgerTasks);
foreach (var burger in burgers)
{
Eat(burger);
}
Zjeść burgery możemy również równolegle, ale ja już postanowiłem je zjeść po kolei.