Automatycznie przydzielanie zadań do wątków

Automatycznie przydzielanie zadań do wątków
AD
  • Rejestracja:około 14 lat
  • Ostatnio:ponad 12 lat
  • Postów:7
1

Witam,

Mam taki problem.

Chciałbym w aplikacji określić liczbę wątków np. 3.
Mam około powiedzmy 30 zadań, które mogą być wykonywane niezależnie od siebie, ale zanim program przejdzie dalej wszystkie muszą zostać wykonane. Liczba wątków jest dość mała, gdyż każde zadanie potrzebuje sporej ilości pamięci RAM. Gdybym uruchomił 30 na raz to program spowolniłby cały komputer, gdyż na pewno zabrakłoby pamięci...

Zrobiłem już coś takiego, że tworzę 3 wątki, dodaje 3 pierwsze zadania ale następnie czekam (z metodą .join()) aż te trzy się skończą i dopiero uruchamiam kolejne trzy.

Moje pytanie jest. Czy w Javie można zrobić coś takiego, że uruchamiam 3 zadania na 3 wątkach a gdy jedno z zadań zakończy się automatycznie przydzielam następne zadanie tak, aby zawsze pracowała określona liczba wątków? Chodzi po prostu oto, aby maksymalnie wykorzystać określoną liczbę wątków.

Drugie pytanie czy w takim wypadku nie lepiej wykorzystać programowanie równoległe i rozdzielić zadania na poszczególne procesory? Czy w takim razie w Javie można wtedy automatycznie przydzielać zadania do procesorów?

Z góry dzięki za pomoc!

Pozdrawiam,
adalgrim

Koziołek
Moderator
  • Rejestracja:prawie 18 lat
  • Ostatnio:około miesiąc
  • Lokalizacja:Stacktrace
  • Postów:6821
1

Odpowiedź na pytanie numer dwa brzmi nie. Program w Javie nie wie tak naprawdę nic o tym jak wygląda świat poza JVM.

Odpowiedź na pytanie brzmi ExecutorService, ThreadPoolExecutor oraz CountDownLatch. To wszystko dostępne w standardowym API języka.


Sięgam tam, gdzie wzrok nie sięga… a tam NullPointerException
Olamagato
Program nie musi wiedzieć co się dzieje poza JVM. Grunt, żeby JVM dobrze modelował ten świat w swoim środowisku. A robi to przyzwoicie na pingwinach i oknach (tym łatwiej że obsługa wątków jest od pewnego czasu natywna). Gdyby pytanie padło rok temu, to napisałbym, że najlepszy model dawałby Executors.newFixedThreadPool() z parametrem System.getAvailableProcessors() i sensownie zmajstrowaną własną fabryką. Ale dzisiaj nawet to nie jest aktualne.
Kerai
  • Rejestracja:ponad 16 lat
  • Ostatnio:ponad 2 lata
  • Lokalizacja:London
0
adalgrim napisał(a)

Moje pytanie jest. Czy w Javie można zrobić coś takiego, że uruchamiam 3 zadania na 3 wątkach a gdy jedno z zadań zakończy się automatycznie przydzielam następne zadanie tak, aby zawsze pracowała określona liczba wątków? Chodzi po prostu oto, aby maksymalnie wykorzystać określoną liczbę wątków.

Drugie pytanie czy w takim wypadku nie lepiej wykorzystać programowanie równoległe i rozdzielić zadania na poszczególne procesory? Czy w takim razie w Javie można wtedy automatycznie przydzielać zadania do procesorów?

http://www.ibm.com/developerworks/library/j-jtp0730/index.html

edytowany 1x, ostatnio: Kerai
Olamagato
  • Rejestracja:ponad 16 lat
  • Ostatnio:9 dni
  • Lokalizacja:Polska, Warszawa
  • Postów:1058
1

Jest obecnie tylko jedna naprawdę dobra odpowiedź, która wyczerpuje całkowicie Twoje pytanie i pojawiła się oficjalnie pół roku temu. Brzmi ona:
"ForkJoinPool" - z zadaniami, które dziedziczą po ForkJoinTask<V> (typowo RecursiveTask i RecursiveAction, ale nie koniecznie):
http://download.oracle.com/javase/tutorial/essential/concurrency/forkjoin.html
http://download.oracle.com/javase/7/docs/api/java/util/concurrent/ForkJoinPool.html
http://download.oracle.com/javase/7/docs/api/java/util/concurrent/ForkJoinTask.html
Trochę info z czasów, kiedy ten executor nie był jeszcze częścią Javy:
http://www.javacodegeeks.com/2011/02/java-forkjoin-parallel-programming.html

Ta pula wątków domyślnie obsługuje ilość wątków równą ilości dostępnych procesorów, ale można to zmienić i wymusić od 1 do 32K wątków w puli. Jeżeli zadania równomiernie dzielą zadanie główne na części, to każdy z wątków będzie rozplanowany przez systemowego managera wątków na osobnym rdzeniu/procesorze. W efekcie dostaniesz dokładnie to czego potrzebujesz - maksymalną wydajność skalowaną ilością rdzeni. W przypadku procesora jednojajowego wszystko sprowadzi się do sekwencyjnego wywoływania kolejnych utworzonych obiektów zadań (np. przez fork(), invoke()) bo wątek roboczy w puli będzie tylko jeden. Tak więc na takim procku będzie niewielki narzut marnowanej mocy przetwarzania, ale za to na maszynach wieloprocesorowych całe zadanie wykona się tak szybko jak dobrze uda Ci się podzielić zadanie na pod-zadania. Zadania można robić zupełnie odmienne i niezależne od siebie. W takim wypadku trudniej tak wszystko dopasować, aby ich czas wykonywania był mniej więcej podobny. Jednak nawet jeżeli to się nie uda, to każde zadanie, które się szybciej zakończy zwolni wątek, który pobierze sobie z wewnętrznej kolejki następne zadanie do wykonania. Dlatego im mniej jest dostępnych wątków, tym ważniejsze jest aby zadania były podobnej długości i jednocześnie niezbyt krótkie. Bo tylko mocno obciążone wątki są przerzucane przez systemowy manager wątków pod kontrolę różnych rdzeni. Z drugiej strony jeżeli zadań jest dużo (30 spokojnie wystarczy), to ten wykonawca niemal to gwarantuje.
Zrywa on ostatecznie powiązanie zadania z wątkiem, co jest zaletą bo wreszcie nie trzeba zajmować się zarządzaniem wątkami, na rzecz zajęcia się dobrym rozplanowaniem podziału pracy.
Teraz praktycznie każdy algorytm można spróbować przerobić na wersję równoległą. Dzięki temu wykonawcy jest to dużo łatwiejsze w Javie niż kiedykolwiek wcześniej i łatwiejsze niż w jakimkolwiek języku, który podobnego mechanizmu w ogóle nie ma.

Jakie minusy?

  1. Jest utrudnione korzystanie z synchronizatorów takich jak CountDownLatch, CyclicBarrier bo w skrajnym przypadku jednego wątku (czyli jednego procesora) cały mechanizm ForkJoinTasków musi ostro kombinować, żeby się nie zablokować lub nie zakleszczyć (a to kosztuje trochę magii i może się nie udać). Rozwiązanie jest proste - nie trzeba ich wcale używać.
  2. Przy nieumiejętnym podziale zadania - szczególnie jeżeli wadliwie wykorzysta się dynamiczne zarządzaniem podziałami zadania - można zająć za dużo zasobów na raz, co zwykle kończy się wysypką StackOverflow. Na szczęście pierwsza próba złego rozdziału od razu sypie wyjątkami, więc wiadomo że się skaszaniło. Z drugiej strony stackTrace nie za wiele daje informacji o miejscu sypnięcia ponieważ nierzadko ma naturę taką jak samo zadanie - rekurencyjną.
    Ale coś za coś.

ps. Zapomniałem jeszcze napisać, że ForkJoinTask<V> posiada trzy bardzo przydatne metody "adapt", które konwertują zwykłe zadania Runnable lub Callable.


Jeżeli ktoś komuś coś, ewentualnie nikt nikomu nic, to właściwie po co...?
edytowany 4x, ostatnio: Olamagato
Koziołek
Moderator
  • Rejestracja:prawie 18 lat
  • Ostatnio:około miesiąc
  • Lokalizacja:Stacktrace
  • Postów:6821
0

@Olamagato, ale to jest dostępne dopiero od wersji 7. Zatem jeżeli masz możliwość wykorzystania 7 to ok. Jednak nie znam jeszcze żadnego większego projektu, który by przeszedł w pełni na Javę 7. Ona jeszcze się nieuleżała.


Sięgam tam, gdzie wzrok nie sięga… a tam NullPointerException
Olamagato
Prawda, ale ktoś kto ma projekt tej wielkości, raczej nie wypisuje prośby o tego typu pomoc na forum. A jeżeli chodzi o aplikacje nowe, to nie ma żadnych ogólnych przeciwwskazań żeby od razu przejść na siódemkę. To jest właśnie jedna z zalet przejścia.
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)