Proudy. Třída vlákna a rozhraní Runnable. Závit třídy Zdvořilostní vlákno
- Jaké jsou priority vlákna?
Odpověď na tuto otázku je v přednáškách JavaRush.
Pro optimalizaci paralelního provozu vláken v Javě je možné upřednostňovat vlákna. Vlákna s vyšší prioritou mají výhodu v získávání času procesoru nad vlákny s nižší prioritou.
Práce s prioritami jsou poskytovány následujícími metodami třídy vlákna:
veřejná konečná neplatná sadaPriority (int newPriority)
Nastavuje prioritu vlákna.
veřejné finální int getPriority ()
Umožňuje znát prioritu vlákna.
Hodnota parametru v metodě setPriority nemůže být libovolná. Musí být mezi MIN_PRIORITY a MAX_PRIORITY. Když je vytvořeno, vlákno má přednost NORM_PRIORITY.
MIN_PRIORITY \u003d 1.
NORM_PRIORITY \u003d 5.
MAX_PRIORITY \u003d 10. - vytvořit příliš mnoho podprocesů a zdroje jsou zbytečné a čas také zbytečný vytvářením nepoužitých podprocesů
- zničit příliš mnoho vláken a více času bude stráveno později jejich vytvořením
- příliš dlouhé vytváření vláken může vést ke špatnému výkonu klienta (dlouhé čekací doby)
Je možné zastavit vlákno snížením jeho priority na 0?
Odpověď v článku: „Top 50 otázek při rozhovoru. Téma: Vícevláknové »
Nalezeno na fóru.
V tomto článku je anglická verze: 50 nejčastějších dotazů na rozhovor s vlákny Java pro osvěžující, zkušené programátory
Java poskytuje bohatá API pro všechno, ale paradoxně neposkytuje vhodné způsoby, jak zastavit vlákno. V JDK 1.0 bylo několik kontrolních metod, například stop (), suspend () a resume (), které byly označeny jako zastaralé v budoucích verzích kvůli potenciálním hrozbám zablokování, vývojáři Java API se od té doby nepokusili poskytnout robustní, bezpečný a elegantní způsob zastavení vláken. Programátoři většinou spoléhají na skutečnost, že se vlákno zastaví, jakmile dokončí provádění metod run () nebo call (). Pro ruční zastavení programátoři využívají těkavé booleovské proměnné a kontrolují její hodnotu při každé iteraci, pokud v metodě run () existují smyčky nebo přerušují vlákna pomocí metody přerušení () k náhlému zrušení úkolů.
Konkrétně k problému: Nikdy jsem neviděl nikoho upřednostňovat na 0.
Pokud o tom někdo ví, napište do komentářů.
Proč potřebujeme třídu ThreadGroup?
ThreadGroup je sada podprocesů, které mohou obsahovat i jiné skupiny podprocesů. Skupina vláken tvoří strom, ve kterém každá další skupina vláken má nadřazeného (kromě původního). Vlákno má právo přístupu k datům ze své skupiny vláken, ale nemá takový přístup k jiným skupinám nebo nadřazené skupině vláken.
Z které skupiny vláken se skládá hlavní vlákno?
Nikde jsem to nenašel)) Řekněte mi, kde to je))
Co je to vzor ThreadPool?
K dispozici je výňatek z článku wikipedia:
V počítačovém programování je vzorec podprocesů (také replikovaní pracovníci nebo model pracovníka-posádky) tam, kde je vytvořeno množství podprocesů pro provádění řady úkolů, které jsou obvykle uspořádány ve frontě. Výsledky z prováděných úkolů mohou být také umístěny do fronty nebo úkoly nemusí vracet žádný výsledek (například pokud jde o animaci). Obvykle existuje mnohem více úkolů než vláken. Jakmile vlákno dokončí svůj úkol, vyžádá si další úlohu z fronty, dokud nebudou dokončeny všechny úkoly. Podproces pak může být ukončen nebo spán, dokud nebudou k dispozici nové úkoly.
Počet použitých vláken je parametr, který lze vyladit tak, aby poskytoval nejlepší výkon. Počet vláken může být navíc dynamický na základě počtu čekajících úkolů. Webový server může například přidat vlákna, pokud přijde řada požadavků na webovou stránku, a může je odstranit, když se tyto požadavky zužují. Náklady na větší fond vláken jsou zvýšené využití zdrojů. Algoritmus použitý k určení, kdy vytvořit nebo zničit vlákna, bude mít dopad na celkový výkon:
V počítačovém programování existuje model fondu podprocesů, ve kterém je vytvořen určitý počet podprocesů pro provádění řady úkolů, které jsou obvykle uspořádány ve frontě. Výsledky z dokončených úkolů lze také zařazovat do fronty nebo úkoly nemusí vracet žádný výsledek (například pokud jde o animaci).
Zpravidla existuje mnohem více úkolů než vláken. Jakmile vlákno dokončí svou úlohu, vyžádá si další úlohu z fronty, dokud nebudou dokončeny všechny úkoly. Tok pak může být přerušen nebo usnout. Počet použitých vláken je parametr, který lze vyladit tak, aby poskytoval nejlepší výkon. Kromě toho může být počet vláken dynamický na základě počtu úkolů, které vyvstávají. Například webový server může přidat toky, pokud dorazí požadavky na více webových stránek, a může vymazat toky, když je méně požadavků. Jak se velikost fondu podprocesů zvětšuje, zvyšuje se využívání počítačových prostředků. Algoritmus použitý k určení, kdy vytvořit nebo zničit vlákna, bude mít vliv na celkový výkon: - Příliš mnoho vláken znamená plýtvání zdroji a časem.
Zničte příliš mnoho podprocesů a více času strávíme později jejich vytvořením - příliš pomalé vytváření podprocesů může vést ke snížení výkonu klienta.
Proč je potřeba třída ThreadPoolExecutor?
veřejná třída ThreadPoolExecutor rozšiřuje službu AbstractExecutorService
Služba ExecutorService, která provádí každou odeslanou úlohu pomocí jednoho z několika možných sdružovacích vláken, obvykle konfigurovaných pomocí továrních metod prováděcích programů.
Fondy podprocesů řeší dva různé problémy: obvykle poskytují zlepšený výkon prováděním velkého množství asynchronních úkolů v důsledku snížené režie volání pro úlohu a poskytují prostředky pro omezení a správu zdrojů, včetně podprocesů použitých k dokončení sady úkolů. Každý ThreadPoolExecutor také podporuje některé základní statistiky, například počet dokončených úkolů.
Aby byla tato třída užitečná v celé řadě souvislostí, poskytuje mnoho nastavitelných parametrů a páky rozšiřitelnosti. Programátoři jsou však přesvědčeni, že budou používat výhodnější tovární metody Executorů Executor.newCachedThreadPool () (neomezený fond podprocesů, s automatickým obnovením podprocesů), Executor.newFixedThreadPool (int) (fond podprocesů pevné velikosti) a Executor.newSingleThreadExecutor () (jediné pozadí podprocesu) které předkonfigurují nastavení pro nejběžnější případy použití.
Kolik způsobů vytvoření vlákna znáte?
Na jazykové úrovni existují dva způsoby, jak vytvořit vlákno. Objekt třídy java.lang.Thread je vlákno, ale potřebuje k provedení úkolu, což je objekt, který implementuje rozhraní java.lang.Runnable. Protože třída vlákna implementuje rozhraní Runnable, můžete přepsat metodu run () zděděním vaší třídy od vlákna nebo implementací rozhraní Runnable do ní.
Na co se třída Future používá?
Budoucnost ukládá výsledek asynchronního výpočtu. Výpočet můžete zahájit tak, že někomu poskytnete budoucí objekt a zapomenete na něj. Vlastník objektu Future může získat výsledek, až bude připraven.
Jaké jsou výhody Callable oproti Runnable?
Rozhraní Callable je mnohem vhodnější pro vytváření úkolů určených pro paralelní provádění než rozhraní Runnable nebo Thread. Je třeba poznamenat, že schopnost přidat takové rozhraní se objevila až od Java 5, protože klíčovou vlastností rozhraní Callable je použití parametrizovaných typů (generik), jak je uvedeno v seznamu.
Výpis Vytvoření úlohy pomocí importovatelného java rozhraní Callable 10 1. util. souběžné. Volaný 11 2 veřejná třída CallableSample implementuje Callable (12 3 public String call () vyvolá výjimku (13 4 if (některé podmínky) (14 5 vyvolá novou IOException ( "chyba během zpracování úlohy"); 15 6) 16 7 Systém. ven. println ("úloha se zpracovává"); 17 8 návrat "výsledek"; 18 9) 19 10)
Okamžitě je třeba věnovat pozornost řádku 2, kde je uvedeno, že rozhraní Callable je parametrizováno, a jeho konkrétní implementace - třída CallableSample, závisí na typu řetězce. Řádek 3 ukazuje podpis metody hlavního volání v již parametrizované verzi, protože typ řetězce je také určen jako typ návratu. Ve skutečnosti to znamená, že byla vytvořena úloha, jejímž výsledkem bude objekt typu String (viz řádek 8). Stejným způsobem můžete vytvořit úlohu, v jejímž důsledku bude objekt jakéhokoli požadovaného typu vytvořen a vrácen v metodě volání. Takové řešení je mnohem pohodlnější ve srovnání s metodou run v rozhraní Runnable, které nevrací nic (jeho návratový typ je neplatný), a proto je nutné vynalézt náhradní řešení, aby bylo možné extrahovat výsledek úkolu.
Další výhodou rozhraní Callable je schopnost „vyvolávat“ výjimky bez ovlivnění probíhajících úkolů. Řádek 3 označuje, že výjimka typu Výjimka může být „vyhozena“ z metody, což ve skutečnosti znamená jakoukoli výjimku, protože všechny výjimky jsou potomci java.lang.Exception. Na řádku 5 se tato funkce používá k vytvoření kontrolované (zaškrtnuté) výjimky typu IOException. Metoda běhu runnable interface nedovolila házení řízených výjimek vůbec a házení nekontrolované (runtime) výjimky způsobilo zastavení podprocesu a celé aplikace.
Je možné úlohu zrušit, pokud používáte třídu Future?
Na základě této diskuse, zvednuté na náboji, se ukazuje, že to není možné.
Future má metodu Future.cancel (boolean), která by měla zrušit provedení úkolu. Pokud ale úloha již začala, volání Future.cancel (true) ji opravdu nezastaví. V útrobách implementace FutureTask běží kód:
if (mayInterruptIfRunning) (vlákno r \u003d runner; if (r! \u003d null) r. interrupt ();)Ty. Opět se podproces, ve kterém je úloha spuštěna, doporučuje pouze zastavit provádění. Navíc nemáme ani možnost zjistit, zda je úkol aktuálně prováděn nebo ne. Existuje metoda Future.isDone (), ale opět se vrací skutečný nejen po dokončení úlohy, ale ihned po volání Future.cancel (), i když je úloha stále spuštěna (Future.cancel (true) nakonec nezastaví úlohu, která již byla spuštěna).
Pokud tedy sami napíšeme celý kód, můžete Thread.isInterrupted () na správných místech pečlivě zpracovat a vše bude v pořádku. Ale pokud spustíme kód třetí strany? Měli bychom mít rozšiřitelný server s pluginy? Jakýkoli zkreslený plugin může snadno vést k nefunkčnímu stavu celého serveru, protože nemůžeme správně přerušit provádění zmrazeného pluginu.
V ruské terminologii za tímto termínem Vlákno Posílený překlad „Stream“. Ačkoli toto slovo lze také přeložit jako „vlákno“. Někdy je v zahraničních vzdělávacích materiálech pojem toku vysvětlen přesně na vláknech. Pokračujeme v logické řadě - tam, kde jsou vlákna, je koule. A kde je míč, je tu kočka. Je zřejmé, že překladatelé neměli kočky. Tak vznikl zmatek. Pod tímto termínem jsou navíc další vlákna Proud. Překladatelé jsou obecně podivní lidé.
Při spuštění jakékoli aplikace se nazývá vlákno hlavní proud (hlavní). Z toho jsou generovány dětské toky. Hlavní vlákno je obvykle poslední vlákno, které program ukončí.
Přestože je hlavní vlákno vytvořeno automaticky, lze jej ovládat prostřednictvím objektu třídy Vlákno. Chcete-li to provést, volejte metodu currentThread (), po kterém můžete řídit tok.
Třída Vlákno obsahuje několik metod pro správu toků.
- getName () - získejte název streamu
- getPriority () - získat prioritu vlákna
- je naživu () - určit, zda vlákno běží
- připojit () - čekat na dokončení toku
- run () - spusťte stream. Napište do něj kód
- spánek () - pozastavit proud na daný čas
- start () - zahájit proud
Získáme informace o hlavním vláknu a změníme jeho název.
Thread mainThread \u003d Thread.currentThread (); mInfoTextView.setText ("Aktuální vlákno:" + mainThread.getName ()); // Změnit název a zobrazit v textovém poli mainThread.setName ("CatThread"); mInfoTextView.append ("\\ nNové vlákno název:" + mainThread.getName ());
Výchozí název hlavního vlákna hlavníkteré jsme nahradili Catthread.
Budeme volat informace o názvu proudu bez zadání metody.
Thread mainThread \u003d Thread.currentThread (); mInfoTextView.setText ("Aktuální vlákno:" + mainThread);
V tomto případě můžete vidět řádek Vlákno - název proudu, jeho prioritu a název jeho skupiny.
Vytvořte si vlastní stream
Vytvoření vlastního proudu není obtížné. Stačí se zdědit od třídy Vlákno.
Vyhlašujeme vnitřní třídu uvnitř naší třídy a voláme ji kliknutím, voláním metody start ().
Veřejná třída MyThread rozšiřuje vlákno (public void run () (Log.d (TAG, "My thread is running ..."));)) public void onClick (Zobrazit pohled) (MyThread myThread \u003d new MyThread (); myThread.start ( );)
Nebo převeďte volání metody start () konstruktorovi.
Veřejné neplatné onClick (zobrazení pohledu) (MyThread myThread \u003d new MyThread ();) veřejná třída MyThread rozšiřuje vlákno (// Constructor MyThread () (// Vytvořit nový podproces super ("druhé vlákno"); Log.i (TAG, ") Druhý tok je vytvořen "+ toto"; start (); // Zahájení toku) public void run () (Log.d (TAG, "Můj stream běží ...")); zkuste (pro (int i \u003d 5; i\u003e) 0; i--) (Log.i (TAG, "Druhé vlákno:" + i); Thread.sleep (500);)) úlovek (InterrupttedException e) (Log.i (TAG, "Druhé vlákno přerušeno"); )))
Vytvoření spustitelného vlákna
Pro vytvoření streamu existuje složitější možnost. Chcete-li vytvořit nové vlákno, musíte implementovat rozhraní Runnable. Stream můžete vytvořit z libovolného objektu, který implementuje rozhraní. Runnable a deklarovat metodu run ().
Metoda uvnitř run () odesíláte kód pro nové vlákno. Toto vlákno skončí, jakmile se metoda vrátí.
Když deklarujete novou třídu s rozhraním Runnablemusíte použít konstruktor:
Vlákno (Runnable thread_object, String thread_name)
První parametr určuje instanci třídy, která implementuje rozhraní. Určuje, kde začne provádění podprocesu. Druhý parametr předává název proudu.
Po vytvoření nového vlákna je třeba jej spustit pomocí této metody start (), což v zásadě vyvolá volání metody run ().
Vytvořte nové vlákno uvnitř projektu školení jako vnořenou třídu a spusťte jej.
Balíček en.alexanderklimov.expresscourse; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import statického ru.alexanderklimov.expresscourse.R.id.textViewInfo; veřejná třída MainActivity rozšiřuje AppCompatActivity (final String TAG \u003d "ExpressCourse"; soukromé tlačítko mButton; soukromé EditText mResultEditText; soukromé TextView mInfoTextView; @Override chráněné void onCreate (Bundle uloženoInstanceState) (super.onCreate (uloženéInstanceState). ); mButton \u003d (Button) findViewById (R.id.buttonGetResult); mResultEditText \u003d (EditText) findViewById (R.id.editText); mInfoTextView \u003d (TextView) findViewById (textViewInfo);) veřejné neplatné onClick (zobrazení pohledu) MyRunnable (); // vytvořit nový pokus o vlákno (pro (int i \u003d 5; i\u003e 0; i--) (Log.i (TAG, "Hlavní vlákno:" + i); Thread.sleep (1000);)) ) catch (InterruptedException e) (Log.i (TAG, "Hlavní vlákno přerušeno");)) třída MyRunnable implementuje Runnable (vlákno podprocesu; // Konstruktor MyRunnable () (// Vytvoření nového druhého vlákna podprocesu \u003d nové vlákno (toto, "Příklad vlákna"); Log.i (TAG, "Vytvořeno druhé vlákno" + vlákno); vlákno.start (); // Spustit p otok) // Požadovaná metoda pro veřejné neplatné běhové rozhraní Runnable () (try (pro (int i \u003d 5; i\u003e 0; i--) (Log.i (TAG, "Druhé vlákno:" + i); Thread.sleep (500);)) úlovek (InterrupttedException e) (Log.i (TAG, "Druhé vlákno přerušeno");))) )))
Uvnitř konstruktoru MyRunnable () vytváříme nový objekt třídy Vlákno
Thread \u003d new Thread (this, "Thread například");
První parametr použil objekt tento, což znamená touha zavolat metodu run () tento objekt. Dále je metoda volána. start ()v důsledku toho začíná provádění podprocesu, počínaje metodou run (). Metoda zase začíná smyčku pro naše vlákno. Po vyvolání metody start ()konstruktér MyRunnable () vrátí ovládání aplikaci. Když hlavní vlákno pokračuje ve své práci, vstupuje do svého cyklu. Poté jsou obě vlákna spuštěna paralelně.
Kromě prvního vlákna můžete spouštět více vláken, nejen druhé vlákno. To může vést k problémům, když se dva vlákna pokusí pracovat se stejnou proměnnou současně.
Synchronizované klíčové slovo - synchronizované metody
K vyřešení problému s vlákny, které mohou být matoucí, se používá synchronizace.
Metoda může mít modifikátor. synchronizované. Pokud je vlákno uvnitř synchronizované metody, měly by počkat všechny ostatní vlákna, které se jej pokusí volat ve stejné instanci. To eliminuje zmatek, když se více vláken pokusí vyvolat metodu.
Syncronized void meow (String msg);
Kromě toho klíčové slovo synchronizované lze použít jako operátor. Můžete uzavřít do bloku synchronizované volání metody některé třídy:
Syncronized (object) (// příkazy vyžadující synchronizaci)
Looper
Stream má entity Looper, Psovod, MessageQueue.
Každé vlákno má jeden jedinečný Looper a může mít hodně Psovod.
Počet Looper objekt pomocného proudu, který jej řídí. Zpracovává příchozí zprávy a také dává pokyn podprocesu k ukončení ve správný čas.
Stream dostane svůj Looper a MessageQueue prostřednictvím metody Looper.prepare () po spuštění. Looper.prepare () identifikuje volající vlákno, vytvoří Looper a MessageQueue a sváže je k nim v úložišti Threadlocal. Metoda Looper.loop () by měl být povolán ke spuštění Looper. Tuto metodu můžete dokončit pomocí této metody looper.quit ().
Třída LooperThread rozšiřuje vlákno (public Handler mHandler; public void run () (Looper.prepare (); mHandler \u003d new Handler () (public void handleMessage (Message msg) (// zde zpracovává příchozí zprávy)); Looper.loop () ;))
Použijte statickou metodu getMainLooper () mít přístup Looper hlavní vlákno:
Looper mainLooper \u003d Looper.getMainLooper ();
Vytvořte dvě vlákna. Začneme jeden v hlavním toku a druhý odděleně od hlavního. Stačí nám dvě tlačítka a štítek.
Všimněte si, jak vlákna začínají. První vlákno se spustí pomocí metody start ()a za druhé - run (). Pak zkontrolujeme, v jakém proudu jsme.
Balíček ru.alexanderklimov.as23; import android.os.Bundle; import android.os.Looper; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.TextView; veřejná třída MainActivity rozšiřuje AppCompatActivity (TextView mInfoTextView; @Override chráněné void onCreate (Bundle uloženéInstanceState) (super.onCreate (uloženéInstanceState); setContentView (R.layout.activity_main); Button startButton \u003d (Button) findViewByIut_d.art Button runButton \u003d (Button) findViewById (R.id.button_run); mInfoTextView \u003d (TextView) findViewById (R.id.textview_info); startButton.setOnClickListener (nové zobrazení.OnClickListener () (@Override veřejné void onClick (Zobrazit pohled)) Thread thread \u003d new Thread (new MyRunnable ()); thread.start (); // v podprocesu na pozadí))); runButton.setOnClickListener (nové zobrazení. \u003d new Thread (new MyRunnable ()); thread.run (); // v aktuálním vláknu)));)) soukromá třída MyRunnable implementuje Runnable (@Override public void run () (// Zkontrolujte, ve kterém vláknu jsme, pokud ((( Looper.getMainLooper (). GetThread () \u003d\u003d Thread.currentThread ()) (mInfoText View.setText ("V hlavním proudu"); ) else (runOnUiThread (new Runnable () (@Override public void run () (mInfoTextView.setText ("V podprocesu na pozadí");))))))))))))
Toto téma je poměrně složité a pro většinu to není zajímavé a není třeba ho studovat.
V Androidu se stále více používají čisté toky, systém má své vlastní způsoby.
Do jakých stavů může vlákno vstoupit při vstupu do synchronizovaného bloku?
- RUNNABLE
- BLOKOVÁNO
V RUNNABLE, pokud blok kódu označený synchronizovaný není zaneprázdněn jiným vláknem. Jinak naše vlákno obdrží stav BLOCKED a bude čekat na uvolnění mutexového objektu.
Volání této metody uvede vlákno do stavu Čekání.
Metodu wait () lze vyvolat pouze uvnitř synchronizovaného bloku objektu mutex, který byl aktuálním vláknem „uzamčen (uzamčen)“, jinak způsob vyvolá výjimku IllegalMonitorStateException.
Object monitor \u003d getMonitor (); synchronizovaný (monitor) (... monitor. wait (); ...)
Když je volána metoda wait (), aktuální vlákno odemkne objekt monitoru a vstoupí do stavu WAITING, čeká na metodu monitor.notify () nebo monitor.notifyAll (), která bude vyvolána jiným vláknem. Jakmile se to stane, vlákno se probudí a pokud monitor nebyl zaneprázdněn, zachytí jej a bude pokračovat v práci.
Do jakého stavu se vlákno přepne, když se volá metoda wait (500)?
Volání této metody přepne vlákno do stavu TIMED_WAITING.
Analogicky s metodou wait (), wait (timeout) lze volat pouze uvnitř synchronizovaného bloku objektu mutex, který byl „uzamčen (uzamčen)“ aktuálním vláknem. Object monitor \u003d getMonitor (); synchronizovaný (monitor) (... monitor. počkejte (500); ...)
Když se volá metoda wait (), aktuální vlákno odemkne objekt monitoru a usne po dobu 500 milisekund. Objekt monitoru může být zachycen jiným vláknem.
Po 500 milisekundách se vlákno probudí a pokud monitor nebyl zaneprázdněn, zachytí jej a bude pokračovat v práci.
Pokud je monitor zaneprázdněn jiným vláknem, aktuální vlákno přejde do stavu BLOCKED.
Do jakého stavu se vlákno dostane, když se vyvolá metoda Notify ()?
Object monitor \u003d getMonitor (); synchronizovaný (monitor) (... monitor. wait (); ...)
Po monitor.wait () vlákno přejde do stavu Čekání. Metoda notify (), volaná jiným vláknem na objektu monitoru, převede vlákno ze stavu WAITING do stavu RUNNABLE, pokud objekt monitoru není zachycen jiným vláknem, jinak je blokován.
Do jakého stavu přejde vlákno, když je volána metoda notifyAll ()?
NotifyAll () "probudí" všechna vlákna. Jeden z podprocesů WAITING vstoupí do stavu RUNNABLE, zachytí monitor použitého objektu a bude pokračovat ve své práci. Zbytek bude ve stavu BLOCKED. Jakmile první „probuzené“ vlákno uvolní monitor, na který všichni ostatní čekají, další vlákno zopakuje svůj osud (libovolné vlákno ze stavu BLOCKED přejde do stavu RUNNABLE). To bude pokračovat, dokud všechna probuzená vlákna neopustí stav BLOCKED.
Tři vlákna v synchronizovaném bloku nazvaná wait () na objektu mutex. Jaký stav budou tato vlákna procházet, pokud čtvrté vlákno bude volatAll ()?
Dva z nich přejdou do stavu BLOCKED, jeden do stavu RUNNABLE
Jaký je rozdíl mezi spojením (500) a čekáním (500)?
Přestože oba spojují (500) a čekají (500), vloží aktuální vlákno do stavu TIMED_WAITING, mezi nimi jsou významné rozdíly:
spojení (500) se volá na vlákně, čekání (500) se vyvolá uvnitř synchronizovaného bloku objektu, přes který je daný blok synchronizován.
Při vyvolání spojení (500) bude aktuální vlákno čekat 500 milisekund, než bude dokončeno vlákno, kterému byla volána metoda join ().
Po 500 milisekundách v obou případech vlákna nadále fungují.
Jaký je rozdíl mezi čekáním (500) a spánkem (500)?
Spánek (500) je vyvolán na vlákně, čekání (500) je vyvoláno uvnitř synchronizovaného bloku objektu, přes který je tento blok synchronizován.
Při volání režimu spánku (500) bude aktuální vlákno čekat 500 milisekund a poté pokračovat ve své práci.
Když je voláno wait (500), aktuální vlákno odemkne synchronizovaný objekt a usne po dobu 500 milisekund.
Do jakého stavu se vlákno dostane, když se volá metoda yield ()?
Když se volá metoda yield (), aktuální vlákno „přeskočí svůj tah“ a java okamžitě přepne na další vlákno. Vlákno z běžícího stavu přejde do stavu připravenosti. Spuštěné a připravené stavy jsou dílčí stavy RUNNABLE.
Třída vlákna
Třída vlákna je nejzákladnější ze všech typů oborů oboru názvů System.Threading. Tato třída představuje objektově orientovaný wrapper kolem dané cesty provádění uvnitř konkrétní AppDomain. Tento typ také definuje sadu metod (statickou i instanční), které vám umožňují vytvářet nová vlákna uvnitř aktuální AppDomain, a také pozastavit, zastavit a zničit konkrétní vlákno. Níže je uveden seznam hlavních statických členů:
CurrentcontextTato vlastnost jen pro čtení vrací kontext, ve kterém je vlákno aktuálně spuštěno.
Aktuální témaTato vlastnost jen pro čtení vrací odkaz na aktuálně provádějící vlákno.
GetDomain (), GetDomainID () Spánek ()Tato metoda pozastaví aktuální vlákno po stanovenou dobu.
Třída vlákna také podporuje několik metod na úrovni instance, z nichž některé jsou popsány v následující tabulce. Zrušení nebo pozastavení aktivního vlákna se obvykle považuje za špatný nápad. Když tak učiníte, existuje šance (i když malá), že vlákno může vytéct své pracovní zatížení, když je narušeno nebo přerušeno.
Člen úrovně instance | Jmenování |
Je naživu | Vrací booleovskou hodnotu označující, zda vlákno běží (a ještě není přerušeno nebo zrušeno) |
Isbackground | Získá nebo nastaví hodnotu označující, zda je tento proud „pozadí“ (podrobněji vysvětleno níže) |
název | Umožňuje nastavit název přátelského textového proudu |
Přednost | Získá nebo nastaví prioritu podprocesu, který může převzít hodnotu z výčtu ThreadPriority. |
Vlákno | Získá stav tohoto vlákna, kterému lze přiřadit hodnotu z výčtu ThreadState. |
Přerušit () | Nařídí CLR, aby co nejdříve přerušil vlákno |
Přerušení () | Přeruší (tj. Pozastaví) aktuální vlákno na zadanou čekací dobu |
Připojit () | Blokuje volající vlákno, dokud neskončí zadané vlákno (vlákno, ve kterém se volá Join ()) |
Životopis () | Obnoví dříve pozastavený stream |
Start () | Nařídí CLR, aby zahájil vlákno co nejdříve |
Pozastavit () | Pozastaví vlákno. Pokud je vlákno již pozastaveno, nemá volání Suspend () žádný účinek |
Získávání statistik o aktuálním vláknu
Připomeňme, že vstupní bod spustitelné sestavy (tj. Metoda Main ()) běží v primárním prováděcím vláknu. Pro ilustraci základního použití typu vlákna předpokládejte, že máte novou konzolovou aplikaci. Jak víte, statická vlastnost Thread.CurrentThread načte objekt Thread, který představuje aktuálně provádějící vlákno. Po obdržení aktuálního proudu můžete zobrazit různé statistiky o něm:
Používání systému; pomocí System.Collections.Generic; pomocí System.Linq; using System.Text; using System.Threading; namespace ConsoleApplication1 (třída Program (static void Main () (Console.Title \u003d "(! LANG: Information on main stream)"; Thread thread = Thread.CurrentThread; thread.Name = "MyThread"; Console.WriteLine(@"Имя домена приложения: {0} ID контекста: {1} Имя потока: {2} Запущен ли поток? {3} Приоритет потока: {4} Состояние потока: {5}", Thread.GetDomain().FriendlyName, Thread.CurrentContext.ContextID, thread.Name, thread.IsAlive, thread.Priority, thread.ThreadState); Console.ReadLine(); } } }!}
Přestože je tento kód víceméně zřejmý, uvědomte si, že třída vlákna podporuje vlastnost nazvanou Název. Pokud explicitně nenastavíte jeho hodnotu, vrátí Name prázdný řetězec. Přiřazení přátelského jména konkrétnímu objektu Thread může výrazně zjednodušit ladění. Během ladicí relace v aplikaci Visual Studio 2010 můžete otevřít okno Threads výběrem položky nabídky Debug -\u003e Windows -\u003e Threads (Debugging -\u003e Windows -\u003e Threads).
U typu vlákna je definována vlastnost s názvem Priorita. Ve výchozím nastavení mají všechna vlákna prioritu Normální. To však lze kdykoli změnit v životním cyklu vlákna pomocí vlastnosti Thread.Priority a přidruženého System.Threading.ThreadPriority výčet:
Public enum ThreadPriority (Lowest, UnderNormal, Normal, // Výchozí hodnota. AboveNormal, Highest)
Při nastavování úrovně priority podprocesu na jinou hodnotu, než je výchozí hodnota (ThreadPriority.Normal), je třeba mít na paměti, že to neposkytuje přímou kontrolu nad tím, jak plánovač podprocesů přepíná vlákna mezi sebou. Ve skutečnosti úroveň priority vlákna poskytuje CLR náznak týkající se důležitosti akcí vlákna. Vlákno s ThreadPriority.High nejvyšší prioritou tedy nemusí být zaručeno, že obdrží nejvyšší prioritu.
Předpokládejme, že píšete potrubí, ve kterém 2 vlákna zpracovávají data pomocí sdílené vyrovnávací paměti. Producentský proud tato data vytváří a spotřebitelský proud je zpracovává (Producent - spotřebitelský problém). Následující kód je nejjednodušší model: pomocí vlákna std :: vlákno jsme vytvořili spotřebitelský proud a vytvoříme data v hlavním proudu.
Void produkovat () (// vytvoří úkol a vloží do fronty) void consume () (// načte data z fronty a zpracuje) int main (int, char **) (std :: thread thr (consume); // vygeneruje stream create (); // vytvářet data pro zpracování thr.join (); // počkat, až funkce funguje, spotřebovat () návrat 0;)
Vynecháme synchronizační mechanismy obou vláken a věnujeme pozornost funkci main (). Pokuste se uhádnout, co je s tímto kódem špatné a jak jej opravit?
Řekněme, že funkce consume () vyvolá výjimku. Protože je tato výjimka vyvolána v podřízeném vlákně, nemůžete ji zachytit a zpracovat v hlavním vlákně. Pokud během rozšíření zásobníku podřízeného vlákna neexistoval vhodný obslužný program výjimek, bude vyvolána funkce std :: terminate (), která ve výchozím nastavení vyvolá funkci abort (). Jinými slovy, pokud nezpracováte výjimku v podprocesu generovaném objektem thr, program se ukončí chybou.
Použití funkce produ () je o něco složitější. Předpokládejme, že tato funkce vyvolá výjimku. První věc, kterou chcete udělat, je zabalit tělo main () do bloku try-catch:
Zkuste (std :: vlákno thr (konzumovat); produkovat (); // vyvolá výjimku thr.join ();) catch (...) ()
Zdá se, že problém byl vyřešen, ale pokud se pokusíte spustit tento kód, program přesto selže. Proč se toto děje? Pojďme to správně.
std :: vlákno
Jak jste již možná uhodli, problém nesouvisí s potrubím, ale v zásadě se správným použitím standardních vláken provádění knihovny. Zejména následující obecná funkce je rovnocenná a má stejné problémy:
Void run (funkce
Než se pustíme do řešení našeho problému, krátce si vybavme, jak funguje vlákno std ::.
1) konstruktor pro inicializaci:
Šablona
Po inicializaci objektu std :: thread se vytvoří nové vlákno, ve kterém je spuštěna funkce fn s možnými argumenty args. Po úspěšném vytvoření začne konkrétní instance objektu představovat tento tok v nadřazeném proudu a ve vlastnostech objektu je nastaven příznak spojitelnosti.
Pamatujte: joinable ~ objekt je spojen s proudem.
2) Čekáme na ukončení provádění vygenerovaného vlákna:
Prázdné vlákno :: join ();
Tato metoda blokuje další provádění nadřazeného vlákna, dokud není podřízené dokončeno. Po úspěšném provedení jej objekt toku přestane reprezentovat, protože náš tok již neexistuje. Spojitelná vlajka je zahozena.
3) Okamžitě „odpojte“ objekt od proudu:
Prázdné vlákno :: detach ();
Jedná se o neblokující metodu. Spojitelný příznak je resetován a podřízené vlákno je ponecháno na vlastních zařízeních a svou práci dokončí někdy později.
4) Destruktor:
Vlákno :: ~ vlákno ();
Destruktor ničí objekt. Navíc, pokud má tento objekt příznak, který lze připojit, zavolá se funkce std :: terminate (), která ve výchozím stavu vyvolá funkci abort ().
Pozornost! Pokud jsme vytvořili objekt a vlákno, ale nevolali spojení nebo odpojení, program selže. V zásadě je to logické - pokud je objekt stále připojen k proudu, musíte s ním něco udělat. Ještě lepší je, nedělat nic a program ukončit (alespoň tak rozhodl výbor pro standardy).
Proto, když ve funkci produ () dojde k výjimce, pokusíme se zničit objekt thr, který je spojitelný.
Omezení
Proč se standardní výbor tak rozhodl, a ne jinak? Nebylo by lepší zavolat v destruktoru join () nebo detach ()? Ukázalo se, že nic lepšího. Podívejme se na oba tyto případy.
Předpokládejme, že máme ve svém destruktoru třídu joining_thread, která volá join ():
Joining_thread :: ~ joining_thread () (join ();)
Poté, před zpracováním výjimky, budeme muset počkat, až podřízené vlákno dokončí práci, protože join () blokuje další spuštění programu. A pokud se tak stalo, vygenerovaný tok skončil v nekonečné smyčce?
Void consume () (while (1) (...)) ... try (joinining_thread thr (consume); throw std :: výjimka ();) catch (...) (// se nemusí brzy stát, nebo vůbec nikdy)
Zjistili jsme, že je lepší nevolávat join () v destruktoru (dokud si nejste jisti, že se jedná o správné zpracování událostí), protože se jedná o blokovací operaci. A co detach ()? Proč tuto metodu neblokování v destruktoru nazvat a umožnit hlavnímu vláknu pokračovat v práci? Předpokládejme, že máme takovou třídu detaching_thread.
Pak ale můžeme dojít k situaci, kdy se vygenerované vlákno pokusí použít prostředek, který již neexistuje, jako v následující situaci:
Zkuste (int data; detaching_thread th (spotřebovat, & data); // v tomto případě spotřebovat bere ukazatel na int jako argument throw std :: výjimka ()) catch (...) (// správně zpracovat výjimku // spotřebovat pokračuje provedeno, ale odkazuje na již smazaný datový objekt)
Tvůrci standardu se tak rozhodli přesunout odpovědnost na programátora - nakonec ví, jak by měl program takové případy řešit. Na základě toho všeho se ukazuje, že standardní knihovna je v rozporu s principem - při vytváření vlákna std :: thread se musíme sami starat o správnou správu zdrojů, tj. Explicitně nazývat připojit nebo odpojit. Z tohoto důvodu někteří programátoři nedoporučují používat objekty std :: thread. Stejně jako nové a smazané, vlákno std :: thread umožňuje vytvářet na jejich základě nástroje vyšší úrovně.
Rozhodnutí
Jedním takovým nástrojem je třída z knihovny Boost boost :: thread_joiner. Odpovídá našemu joining_thread ve výše uvedeném příkladu. Pokud si můžete dovolit používat knihovny třetích stran pro práci s datovými proudy, je lepší to udělat.