Blob Blame History Raw
<?xml version="1.0" encoding="utf-8"?>
<page xmlns="http://projectmallard.org/1.0/" xmlns:its="http://www.w3.org/2005/11/its" xmlns:xi="http://www.w3.org/2003/XInclude" type="topic" id="main-contexts" xml:lang="cs">

  <info>
    <link type="guide" xref="index#specific-how-tos"/>

    <credit type="author copyright">
      <name>Philip Withnall</name>
      <email its:translate="no">philip.withnall@collabora.co.uk</email>
      <years>2014 – 2015</years>
    </credit>

    <include xmlns="http://www.w3.org/2001/XInclude" href="cc-by-sa-3-0.xml"/>

    <desc>Hlavní kontext GLib, vyvolání funkcí v jiných vláknech a smyčka událostí</desc>
  </info>

  <title>Hlavní kontexty GLib</title>

  <synopsis>
    <title>Shrnutí</title>

    <list>
      <item><p>K vyvolání funkcí z jiných vláknech použijte funkci <link href="https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#g-main-context-invoke-full"><code>g_main_context_invoke_full()</code></link>, která předpokládá, že každé vlákno má výchozí hlavní kontext, který běží po celou dobu existence vlákna (<link xref="#g-main-context-invoke-full"/>)</p></item>
      <item><p>Ke spuštění funkce na pozadí bez starostí o použití konkrétního vlákna použijte <link href="https://developer.gnome.org/gio/stable/GTask.html"><code>GTask</code></link> (<link xref="#gtask"/>)</p></item>
      <item><p>Nešetřete používáním asercí ke kontrole, který kontext spouští kterou funkci, a přidávejte tyto aserce hned při prvním psaní kódu. (<link xref="#checking-threading"/>)</p></item>
      <item><p>Výslovně zdokumentujte, ve kterém kontextu funkce očekává, že bude volána, zpětné volání vyvoláno a signál vyslán (<link xref="#using-gmaincontext-in-a-library"/>)</p></item>
      <item><p>Dávejte si pozor na <code>g_idle_add()</code> a podobné funkce, které implicitně používají globální výchozí hlavní kontext (<link xref="#implicit-use-of-the-global-default-main-context"/>)</p></item>
    </list>
  </synopsis>

  <section id="what-is-gmaincontext">
    <title>Co je to <code>GMainContext</code>?</title>

    <p><link href="https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#GMainContext"><code>GMainContext</code></link> je zobecněná implementace <link href="http://en.wikipedia.org/wiki/Event_loop">smyčky událostí</link>, která je vhodná pro implementaci skupinových V/V souborových operací nebo systému widgetů založeného na událostech (jako je GTK+). Je jádrem většiny aplikací GLib. Pochopení <code>GMainContext</code> vyžaduje pochopení <link href="man:poll(2)">poll()</link> a skupinových V/V.</p>

    <p><code>GMainContext</code> má sadu objektů <link href="https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#GSource"><code>GSource</code></link>, které jsou k němu „připojené“. O každém z nich můžeme přemýšlet jako o očekávané události s přiřazenou funkcí zpětného volání, která bude vyvolána, když je událost přijata, nebo obdobně, jako o sadě popisovačů souborů, které se mají kontrolovat. Událostí může být například doběhnutí časovače nebo přijetí dat na soketu. Každá jednotlivá iterace smyčky události:</p>
    <list type="enumerated">
      <item><p>Připraví zdroje, přičemž určí, jestli je některý z nich připraven okamžitě vysílat.</p></item>
      <item><p>Seskupí zdroje a zablokuje aktuální vlákno, dokud není přijata událost pro jeden ze zdrojů.</p></item>
      <item><p>Zkontroluje, který ze zdrojů přijal událost (může jich být několik).</p></item>
      <item><p>Vyšle zpětná volání z těchto zdrojů.</p></item>
    </list>

    <p><link href="https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#mainloop-states">Velmi dobře je to vysvětlené</link> v <link href="https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#GSourceFuncs">dokumentaci k GLib</link>.</p>

    <p>V jádru je <code>GMainContext</code> vlastně jen smyčka <code>poll()</code> s přípravnou, kontrolní a vysílací fází smyčky odpovídající běžnému úvodu a závěru v typické implementaci smyčky <code>poll()</code>, jako je například naslouchání 1 v <link href="http://www.linux-mag.com/id/357/">tomto článku</link>. Typicky je zapotřebí jistá míra složitosti v aplikacích s netriviálním využitím <code>poll()</code> ke sledování seznamů popisovačů souborů, které nás zajímají. <code>GMainContext</code> navíc přidává spoustu užitečné funkcionality, kterou varianta <code>poll()</code> nepodporuje. Z toho nejdůležitější je bezpečné použití ve více vláknech.</p>

    <p><code>GMainContext</code> je z hlediska vláken zcela bezpečný, což znamená, že <code>GSource</code> může být vytvořen v jednom vlákně a napojen na <code>GMainContext</code> běžící v jiném vlákně. (Viz také <link xref="threading"/>). Typickým využitím je umožnit pracovnímu vláknu řídit, kterým soketům <code>GMainContext</code> naslouchá v centrálním V/V vlákně. Každý <code>GMainContext</code> je „získán“ vláknem pro každou iteraci, kterou prochází. Ostatní vlákna nemohou přes <code>GMainContext</code> iterovat bez jeho získání, což zajistí, že <code>GSource</code> a jeho popisovače souborů budou seskupeny jen jedním vláknem v každý okamžik (protože každý <code>GSource</code> je napojen nejvýše na jeden kontext <code>GMainContext</code>). <code>GMAinContext</code> může být během iterací napříč vlákny přehazován, ale je to nákladná operace.</p>

    <p><code>GMainContext</code> se používá místo <code>poll()</code> hlavně kvůli pohodlí, protože transparentně obsluhuje dynamickou správu polí s popisovači souborů předávanými do <code>poll()</code>, hlavně když se pracuje napříč více vlákny. Provádí se to zapouzdřením popisovačů polí do objektů <code>GSource</code>, které rozhodují, jestli mají být tyto popisovače souborů předány do volání <code>poll()</code> v každé „přípravné“ fázi iterace hlavního kontextu.</p>
  </section>

  <section id="what-is-gmainloop">
    <title>Co je to <code>GMainLoop</code>?</title>

    <p>Když si odmyslíme počítání referencí a zamykání, je <link href="https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#GMainLoop"><code>GMainLoop</code></link> v podstatě jen pár následujících řádků kódu (z <link href="https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#g-main-loop-run"><code>g_main_loop_run()</code></link>):</p>
    <code mime="text/x-csrc">loop-&gt;is_running = TRUE;
while (loop-&gt;is_running)
  {
    g_main_context_iteration (context, TRUE);
  }</code>

    <p>Plus čtyři řádky v <link href="https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#g-main-loop-quit"><code>g_main_loop_quit()</code></link>, které nastaví <code>loop-&gt;is_running = FALSE</code> a které způsobí, že se smyčka ukončí, jakmile doběhne aktuální iterace hlavního kontextu.</p>

    <p>Proto je <code>GMainLoop</code> vhodným, a z hlediska vláken bezpečným, způsobem, jak spustit <code>GMainContext</code>, aby zpracovával události, dokud není dosažena požadovaná výstupní podmínka. V tu chvíli by měla být zavolána funkce <code>g_main_loop_quit()</code>. V programech s uživatelským rozhraním je to typicky uživatelovo kliknutí na „Ukončit“. V programech obsluhujících sokety do může být závěrečné zavření soketu.</p>

    <p>Je důležité neplést si hlavní kontext s hlavní smyčkou. Hlavní kontext dělá většinu práce: připravuje seznam zdrojů, čeká na události a obesílá zpětná volání. Hlavní smyčka prostě jen opakovaně cykluje kontextem.</p>
  </section>

  <section id="default-contexts">
    <title>Výchozí kontexty</title>

    <p>Jednou z důležitých vlastností <code>GMainContext</code> je, že podporuje „výchozí“ kontext. Existují dvě úrovně výchozího kontextu: výchozí pro vlákno a výchozí globální. Globální (přístupný přes <code>g_main_context_default()</code>) je spuštěn GTK+ ve chvíli, kdy je zavoláno <code>gtk_main()</code>. Používá se také pro časovače (<code>g_timeout_add()</code>) a zpětná volání při nečinnosti (<code>g_idle_add()</code>) — ty nechceme obesílat, když neběží výchozí kontext! (Viz <link xref="#implicit-use-of-the-global-default-main-context"/>.)</p>

    <p>Výchozí kontexty vláken byly do GLib přidány později (od verze 2.22) a obecně se používají pro V/V operace, které potřebují spouštět a obesílat zpětná volání ve vlákně. Zavoláním <code>g_main_context_push_thread_default()</code> před spuštěním V/V operace se nastaví výchozí kontext vlákna a V/V operace může přidat své zdroje do tohoto kontextu. Kontext pak může být spuštěn v nové hlavní smyčce ve V/V vlákně, což způsobí, že zpětná volání budou obesílána v zásobníku tohoto vlákna namísto v zásobníku vlákna, ve kterém běží globální výchozí kontext. To umožňuje, aby V/V operace běžely celé v odděleném vlákně bez vyloženého předávání konkrétního ukazatele <code>GMainContext</code> všude kolem.</p>

    <p>Naopak, spuštěním dlouho trvající operace s nastaveným konkrétním výchozím kontextem vlákna, může volající kód zaručit, že zpětná volání operace budou vyslána v tomto kontextu, i když operace samotná běží v pracovním vlákně. Jde o princip stojící za <link href="https://developer.gnome.org/gio/stable/GTask.html"><code>GTask</code></link>: když je vytvořen nový objekt <code>GTask</code>, uloží si reference na výchozí kontext aktuálního vlákna a a vyšle jeho dokončovací zpětné volání v tomto kontextu, i když úloha samotná běží pomocí <link href="https://developer.gnome.org/gio/stable/GTask.html#g-task-run-in-thread"><code>g_task_run_in_thread()</code></link>.</p>

    <example>
      <p>Například následující kód spustí <code>GTask</code>, který provede z vlákna souběžně dva zápisy. Zpětná volání pro zápisy budou obeslána v pracovním vlákně, zatímco zpětné volání z úlohy jako celku bude obesláno v <code>interesting_context</code>.</p>

      <code mime="text/x-csrc" style="valid">
typedef struct {
  GMainLoop *main_loop;
  guint n_remaining;
} WriteData;

/* Toto je voláno vždy ve stejném vlákně jako thread_cb(), protože je to
 * vždy vysláno v kontextu @worker_context. */
static void
write_cb (GObject      *source_object,
          GAsyncResult *result,
          gpointer      user_data)
{
  WriteData *data = user_data;
  GOutputStream *stream = G_OUTPUT_STREAM (source_object);
  GError *error = NULL;
  gssize len;

  /* Dokončí zápis */
  len = g_output_stream_write_finish (stream, result, &amp;error);
  if (error != NULL)
    {
      g_error ("Error: %s", error-&gt;message);
      g_error_free (error);
    }

  /* Zkontroluje, jestli byly dokončeny všechny paralelní operace */
  write_data-&gt;n_remaining--;

  if (write_data-&gt;n_remaining == 0)
    {
      g_main_loop_quit (write_data-&gt;main_loop);
    }
}

/* Toto je voláno v novém vlákně */
static void
thread_cb (GTask        *task,
           gpointer      source_object,
           gpointer      task_data,
           GCancellable *cancellable)
{
  /* Tento datový proud přichází z jiného místa v programu: */
  GOutputStream *output_stream1, *output_stream;
  GMainContext *worker_context;
  GBytes *data;
  const guint8 *buf;
  gsize len;

  /* Nastaví pracovní kontext pro zpětná volání zápisu */
  worker_context = g_main_context_new ();
  g_main_context_push_thread_default (worker_context);

  /* Nastaví zápis */
  write_data.n_remaining = 2;
  write_data.main_loop = g_main_loop_new (worker_context, FALSE);

  data = g_task_get_task_data (task);
  buf = g_bytes_get_data (data, &amp;len);

  g_output_stream_write_async (output_stream1, buf, len,
                               G_PRIORITY_DEFAULT, NULL, write_cb,
                               &amp;write_data);
  g_output_stream_write_async (output_stream2, buf, len,
                               G_PRIORITY_DEFAULT, NULL, write_cb,
                               &amp;write_data);

  /* Spustí hlavní smyčku, dokud nejsou dokončeny oba zápisy */
  g_main_loop_run (write_data.main_loop);
  g_task_return_boolean (task, TRUE);  /* ignore errors */

  g_main_loop_unref (write_data.main_loop);

  g_main_context_pop_thread_default (worker_context);
  g_main_context_unref (worker_context);
}

/* Toto může být voláno z kteréhokoliv vlákna. Jeho funkce @callback bude
 * vždy vyslána ve vlákně, které aktuálně vlastní @interesting_context */
void
parallel_writes_async (GBytes              *data,
                       GMainContext        *interesting_context,
                       GCancellable        *cancellable,
                       GAsyncReadyCallback  callback,
                       gpointer             user_data)
{
  GTask *task;

  g_main_context_push_thread_default (interesting_context);

  task = g_task_new (NULL, cancellable, callback, user_data);
  g_task_set_task_data (task, data,
                        (GDestroyNotify) g_bytes_unref);
  g_task_run_in_thread (task, thread_cb);
  g_object_unref (task);

  g_main_context_pop_thread_default (interesting_context);
}</code>
    </example>

    <section id="implicit-use-of-the-global-default-main-context">
      <title>Výchozí použití globálního výchozího hlavního kontextu</title>

      <p>Několik funkcí přidává zdroje do globálního výchozího hlavního kontextu implicitně. Ty <em>nesmí</em> být použity v kódu vláken. Místo toho použijte <code>g_source_attach()</code> s <code>GSource</code> vytvořeným nahrazením funkce z tabulky níže.</p>

      <p>Implicitní použití globálního výchozího hlavního kontextu znamená, že jsou funkce zpětného volání vyvolány v hlavním vlákně, typicky jako výsledek práce práce přenesené zpět z pracovního vlákna do hlavního vlákna.</p>

      <table shade="rows">
        <colgroup><col/></colgroup>
        <colgroup><col/><col/></colgroup>
        <thead>
          <tr>
            <td><p>Nepoužívat</p></td>
            <td><p>Místo toho použít</p></td>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td><p><code>g_timeout_add()</code></p></td>
            <td><p><code>g_timeout_source_new()</code></p></td>
          </tr>
          <tr>
            <td><p><code>g_idle_add()</code></p></td>
            <td><p><code>g_idle_source_new()</code></p></td>
          </tr>
          <tr>
            <td><p><code>g_child_watch_add()</code></p></td>
            <td><p><code>g_child_watch_source_new()</code></p></td>
          </tr>
        </tbody>
      </table>

      <example>
        <p>Takže prodlevu v nějakém výpočtu v pracovním vlákně uděláte pomocí následujícího kódu:</p>
        <code mime="text/x-csrc">
static guint
schedule_computation (guint delay_seconds)
{
  GSource *source = NULL;
  GMainContext *context;
  guint id;

  /* Získat kontext volání. */
  context = g_main_context_get_thread_default ();

  source = g_timeout_source_new_seconds (delay_seconds);
  g_source_set_callback (source, do_computation, NULL, NULL);
  id = g_source_attach (source, context);
  g_source_unref (source);

  /* Když je potřeba, může být ID použito se stejným 
   * @context ke zrušení naplánovaného výpočtu. */
  return id;
}

static void
do_computation (gpointer user_data)
{
  /* … */
}</code>
      </example>
    </section>
  </section>

  <section id="using-gmaincontext-in-a-library">
    <title>Použití <code>GMainContext</code> v knihovně</title>

    <p>Na nejvyšší úrovni kód knihovny nesmí provádět změny v hlavním kontextu, který by mohly ovlivnit provádění aplikace, která používá knihovnu. Například změnou, když je vyslán <code>GSource</code> patřící aplikaci. Existuje řada správných zvyklostí, kterých je dobré se držet, aby tomu tak bylo.</p>

    <p>Nikdy neprovádějte iteraci kontextu vytvořeného mimo knihovnu. Týká se to i výchozího globálního kontextu a výchozího kontextu vlákna. V opačném případě může být <code>GSource</code> vytvořený v aplikaci vyslán, když to aplikace neočekává, což způsobí <link href="http://en.wikipedia.org/wiki/Reentrancy_%28computing%29">problém s opětovným vstupem (reentrance)</link> v kódu aplikace.</p>

    <p>Před zahozením poslední reference z knihovny na kontext vždy odstraňte z hlavního kontextu zdroje <code>GSource</code>, především když mohou být vystaveny aplikaci (například jako výchozí vlákno). V opačném případě může aplikace zachovat referenci na hlavní kontext a pokračovat s jeho iteracemi po návratu z knihovny, což případně může způsobit nechtěné vysílání zdroje v knihovně. Je to to stejné, jako když nepředpokládáte, že zahození poslední reference z knihovny na hlavní kontext tento kontext finalizuje.</p>

    <p>Pokud je knihovna navržena pro použití z více vláken nebo ve stylu kontextů, zdokumentujte vždy, ve kterém kontextu budou jednotlivá zpětná volání vysílána. Například „callbacks will always be dispatched in the context which is the thread-default at the time of the object’s construction“ (zpětná volání budou v době vytváření objektu vždy vyslána v kontextu, který je pro vlákno výchozí). Vývojáři používající API knihovny tuto informaci potřebují.</p>

    <p>Používejte <code>g_main_context_invoke()</code>, abyste zajistili, že zpětné volání bude vysláno ve správném kontextu. Je to mnohem jednodušší, než ruční použití <code>g_idle_source_new()</code> k přenosu práce mezi kontexty. (Viz <link xref="#ensuring-functions-are-called-in-the-right-context"/>.)</p>

    <p>Knihovny by nikdy neměly použít <code>g_main_context_default()</code> (nebo udělat to stejné předáním <code>NULL</code> do parametru typu <code>GMainContext</code>). Vždy uložte a výslovně použijte konkrétní <code>GMainContext</code>, i když často ukazuje na nějaký výchozí kontext. V případě potřeby to v budoucnu usnadní rozdělení kódu do vláken, aniž by to způsobilo těžko laditelné probléme vznikající zpětnými voláními vyvolanými v nesprávném kontextu.</p>

    <p>Věci vždy pište interně jako asynchronní (kde je to vhodné, tak pomocí <link xref="#gtask"><code>GTask</code></link>) a na nejvyšší úrovni API pak udržujte synchronní obalující funkci, kterou můžete implementovat pomocí volání <code>g_main_context_iteration()</code> na příslušný <code>GMainContext</code>. Pro zopakování: umožní to v budoucnu snadnější přepracování kódu. Ukázáno je to na příkladu výše: vlákno používá raději <code>g_output_stream_write_async()</code> namísto <code>g_output_stream_write()</code>.</p>

    <p>Vždy si musí odpovídat počet vložení a vyjmutí výchozího hlavního kontextu vlákna: <code>g_main_context_push_thread_default()</code> a <code>g_main_context_pop_thread_default()</code>.</p>
  </section>

  <section id="ensuring-functions-are-called-in-the-right-context">
    <title>Zajištění, aby funkce byly volány ve správném kontextu</title>

    <p>„Ten pravý kontext“ je výchozí hlavní kontext vlákna pro <em>vlákno, ve kterém má být funkce spuštěna</em>. Předpokládá to typický případ, kdy každé vlákno má <em>jeden</em> hlavní kontext, ve kterém běží hlavní smyčka. Hlavní kontext v podstatě poskytuje práci nebo <link href="http://en.wikipedia.org/wiki/Message_queue">frontu zpráv</link> pro vlákno – něco, v čem vlákno může pravidelně kontrolovat, jestli byla dokončena práce v jiném vlákně. Vložením zprávy do fronty – vyvoláním funkce v jiném hlavním kontextu – způsobí její případné vyslání v onom vlákně.</p>

    <example>
      <p>Například, když aplikace provádí nějaké dlouhé a na výkon procesoru náročné výpočty, měla by je naplánovat ve vlákně na pozadí, aby se neblokovaly aktualizace uživatelského rozhraní v hlavním vlákně. Může ale být zapotřebí výsledky výpočtu zobrazit v uživatelském rozhraní, takže funkce, která to bude mít na starosti, musí být po dokončení výpočtu zavolána v hlavním vlákně.</p>

      <p>Mimo to může být výpočetní funkce omezena jen na jedno vlákno, aby se usnadnilo zamezení potřeby zamykat příliš dat při přístupu k nim. To předpokládá, že ostatní vlákna jsou implementována podobně a tudíž je k většině dat přistupováno jen z jediného vlákna a mezi vlákny se komunikuje pomocí <link href="http://en.wikipedia.org/wiki/Message_passing">předávání zpráv</link>. Umožní to jednotlivým vláknům aktualizovat svá data, kdy se jim to hodí, což významně zjednodušuje zamykání.</p>
   </example>

    <p>U některých funkcí nemusí být žádný důvod se starat o to, ve kterém kontextu jsou spuštěné, protože jsou asynchronní a tudíž kontext neblokují. I přesto je rozumné vědět o tom, který kontext je použit, protože tyto funkce mohou vysílat signály nebo vyvolávat zpětná volání a z důvodu bezpečnosti vlákna je nutné vědět, která vlákna hodlají tyto obsluhy signálů nebo zpětná volání vyvolat.</p>

    <example>
      <p>Například pro průběh volání v <link href="https://developer.gnome.org/gio/stable/GFile.html#g-file-copy-async"><code>g_file_copy_async()</code></link> je zdokumentováno, že se volá ve výchozím vlákně hlavního kontextu v době volání inicializace.</p>
    </example>

    <section id="invocation-core-principle">
      <title>Principy vyvolání</title>

      <p>Klíčový princip vyvolání funkce v konkrétním kontextu je jednoduchý a je probrán níže ve vysvětlení konceptu. V praxi by se místo toho měla použít <link xref="#g-main-context-invoke-full">vhodná metoda <code>g_main_context_invoke_full()</code></link>.</p>

      <p>Do cílového <code>GMainContext</code>, který vyvolá funkci, když je obeslán, musí být přidán <link href="https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#GSource"><code>GSource</code></link>. Tento <code>GSource</code> by většinou měl být zdroj pro nečinnost vytvořený pomocí <link href="https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#g-idle-source-new"><code>g_idle_source_new()</code></link>, ale nemusí tomu tak být nutně. Může jít například i o zdroj časovače, takže ona funkce bude spuštěna po nějaké prodlevě.</p>

      <p><code>GSource</code> bude <link xref="#what-is-gmaincontext">vyslán hned, jak bude připraven</link>, zavoláním funkce na zásobníku vlákna. V případě nečinnosti zdroje to bude hned, jak budou vyslány zdroje s vyšší prioritou – to lze vyladit pomocí parametru s prioritou zdroje v <link href="https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#g-source-set-priority"><code>g_source_set_priority()</code></link>. Typicky je zdroj následně zlikvidován, takže funkce se spustí jen jednou (a když znovu, tak to nemusí být ta samá situace).</p>

      <p>Data mezi vlákny mohou být předávána jako <code>user_data</code> předaná do zpětného volání patřícího <code>GSource</code>. Nastaví se to na zdroji pomocí <link href="https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#g-source-set-callback"><code>g_source_set_callback()</code></link> společně s funkcí zpětného volání, která má být vyvolána. K dispozici je jen jeden ukazatel, takže když potřebujete předat více polí, musíte je zabalit do nějaké struktury, kterou si naalokujete.</p>

      <example>
        <p>Následující příklad demonstruje základní principy, ale existují vhodné metody probrané dále, které vše zjednodušují.</p>

        <code mime="text/x-csrc">
/* Hlavní funkce pro vlákno na pozadí thread1 */
static gpointer
thread1_main (gpointer user_data)
{
  GMainContext *thread1_main_context = user_data;
  GMainLoop *main_loop;

  /* Nastaví kontext vlákna a navždy jej spustí */
  g_main_context_push_thread_default (thread1_main_context);

  main_loop = g_main_loop_new (thread1_main_context, FALSE);
  g_main_loop_run (main_loop);
  g_main_loop_unref (main_loop);

  g_main_context_pop_thread_default (thread1_main_context);
  g_main_context_unref (thread1_main_context);

  return NULL;
}

/* Zapouzdření dat pro obsluhu více proměnných mezi vlákny */
typedef struct {
  gchar   *some_string;  /* owned */
  guint    some_int;
  GObject *some_object;  /* owned */
} MyFuncData;

static void
my_func_data_free (MyFuncData *data)
{
  g_free (data-&gt;some_string);
  g_clear_object (&amp;data-&gt;some_object);
  g_free (data);
}

static void
my_func (const gchar *some_string,
         guint        some_int,
         GObject     *some_object)
{
  /* Zde se dělá něco náročného na čas a procesor! */
}

/* Převede zpětné volání nečinnosti na volání my_func() */
static gboolean
my_func_idle (gpointer user_data)
{
  MyFuncData *data = user_data;

  my_func (data-&gt;some_string, data-&gt;some_int, data-&gt;some_object);

  return G_SOURCE_REMOVE;
}

/* Funkce, která má být volána v hlavním vlákně, aby plánovala volání
 * my_func() ve vlákně thread1 a přitom předala zadané parametry */
static void
invoke_my_func (GMainContext *thread1_main_context,
                const gchar  *some_string,
                guint         some_int,
                GObject      *some_object)
{
  GSource *idle_source;
  MyFuncData *data;

  /* Vytvoří zapouzdření dat pro předání všech požadovaných proměnných
   * mezi vlákny */
  data = g_new0 (MyFuncData, 1);
  data-&gt;some_string = g_strdup (some_string);
  data-&gt;some_int = some_int;
  data-&gt;some_object = g_object_ref (some_object);

  /* Vytvoří nový zdroj nečinnosti, nastaví my_func() jako zpětné volání
   * s nějakými daty, která budou předána mezi vlákny, zvýší prioritu a
   * a naplánuje ji napojením na kontext vlákna thread1 */
  idle_source = g_idle_source_new ();
  g_source_set_callback (idle_source, my_func_idle, data,
                         (GDestroyNotify) my_func_data_free);
  g_source_set_priority (idle_source, G_PRIORITY_DEFAULT);
  g_source_attach (idle_source, thread1_main_context);
  g_source_unref (idle_source);
}

/* Hlavní funkce pro hlavní vlákno */
static void
main (void)
{
  GThread *thread1;
  GMainContext *thread1_main_context;

  /* Spawn a background thread and pass it a reference to its
   * GMainContext. Retain a reference for use in this thread
   * too. */
  thread1_main_context = g_main_context_new ();
  g_thread_new ("thread1", thread1_main,
                g_main_context_ref (thread1_main_context));

  /* Zde je možné například nastavit uživatelské rozhraní */

  /* Vyvolá my_func() v jiném vlákně */
  invoke_my_func (thread1_main_context,
                  "some data which needs passing between threads",
                  123456, some_object);

  /* Zde se pokračuje v jiné práci */
}</code>

        <p>Toto vyvolání je <em style="strong">jednosměrné</em>: zavolá se <code>my_func()</code> v <code>thread1</code>, ale neexistuje žádný způsob, jak vrátit hodnotu do hlavního vlákna. Když to potřebujete udělat, je potřeba znovu použít ten samý princip, tj. vyvolat funkci zpětného volání v hlavním vlákně. Jedná se o přímočaré rozšíření, které zde není potřeba rozebírat.</p>

        <p>Aby byla zachována bezpečnost vlákna, musí být přístup k datům, ke kterým je potenciálně možné přistupovat z více vláken, vzájemně vyloučen pomocí <link href="http://en.wikipedia.org/wiki/Mutual_exclusion">mutexu</link>. Data s potenciálním přístupem z více vláken: <code>thread1_main_context</code>, předaný při rozvětvení do <code>thread1_main</code>, a <code>some_object</code>, reference, do které jsou předána zapouzdřená data. Podstatné je, že GLib zaručuje, že <code>GMainContext</code> je z hlediska vláken bezpečný, takže sdílení <code>thread1_main_context</code> mezi vlákny je bezpečné. Příklad předpokládá, že ostatní kód přistupující k <code>some_object</code> je z hlediska vláken bezpečný.</p>

        <p>Všimněte si, že k proměnným <code>some_string</code> a <code>some_int</code> nelze přistupovat z obou vláken, protože do vlákna <code>thread1</code> jsou místo originálů předávány jejich <code>kopie</code>. Jedná se o standardní techniku používanou z důvodu, aby křížové volání vláken bylo z hlediska vícevláknového zpracování bezpečné, aniž by bylo nutné zamykání. Také se tím předejte problémům se synchronizací při uvolňování <code>some_string</code>.</p>

        <p>A obdobně, reference na <code>some_object</code> je přenášena do vlákna <code>thread1</code>, které řeší problém se synchronizací při likvidaci objektu (viz <link xref="memory-management"/>).</p>

        <p>Namísto jednodušší <code>g_idle_add()</code> je požita raději funkce <code>g_idle_source_new()</code>, takže lze určit připojený <code>GMainContext</code>.</p>
      </example>
    </section>

    <section id="g-main-context-invoke-full">
      <title>Užitečná metoda: <code>g_main_context_invoke_full()</code></title>

      <p>To vše se dá úžasně zjednodušit správnou metodou <link href="https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#g-main-context-invoke-full"><code>g_main_context_invoke_full()</code></link>. Ta vyvolá zpětné volání tak, že <code>GMainContext</code> je během něj vlastněn. Vlastnění hlavního kontextu je téměř vždy to stejné, jako jej spustit, a tím pádem je funkce vyvolána ve vlákně, pro které je určený kontext výchozí. </p>

      <p>V případě, že uživatelská data není nutné po návratu z volání uvolňovat pomocí zpětného volání <code>GDestroyNotify</code>, dá se místo toho použít funkce <code>g_main_context_invoke()</code>.</p>

      <example>
        <p>Upravený předchozí příklad – funkce <code>invoke_my_func()</code> může být nahrazena následovně:</p>

        <code mime="text/x-csrc">
static void
invoke_my_func (GMainContext *thread1_main_context,
                const gchar  *some_string,
                guint         some_int,
                GObject      *some_object)
{
  MyFuncData *data;

  /* Vytvoří zapouzdření dat pro předávání všech požadovaných proměnných
   * mezi vlákny */
  data = g_new0 (MyFuncData, 1);
  data-&gt;some_string = g_strdup (some_string);
  data-&gt;some_int = some_int;
  data-&gt;some_object = g_object_ref (some_object);

  /* Vyvolá funkci */
  g_main_context_invoke_full (thread1_main_context,
                              G_PRIORITY_DEFAULT, my_func_idle,
                              data,
                              (GDestroyNotify) my_func_data_free);
}</code>

        <p>Zamysleme se, co se stane, kdy je <code>invoke_my_func()</code> zavolána z <code>thread1</code>, místo z hlavního vlákna. V původní implementaci by byl nečinný zdroj přidán do kontextu vlákna <code>thread1</code> a vyslán v následující iteraci kontextu (předpokládáme, že nečeká vyslání s vyšší prioritou). Ve vylepšené implementaci si <code>g_main_context_invoke_full()</code> všimne, že uvedený kontext je již vlastněn vláknem (nebo jeho vlastnictví může získat), a zavolá přímo <code>my_func_idle()</code>, namísto aby zdroj připojovala ke kontextu a čekala na vyvolání následující iterace.</p>

        <p>Na tomto jemném rozdílu v chování většinou nesejde, ale je dobré na něj pamatovat, protože může ovlivnit chování při blokování (<code>invoke_my_func()</code> by zabrala nezanedbatelné množství času, které by odpovídalo stejnému množství času jako u <code>my_func()</code> před návratem).</p>
      </example>
    </section>
  </section>

  <section id="checking-threading">
    <title>Kontrola vláken</title>

    <p>Je vhodné zdokumentovat, která funkce je volána ve kterém vlákně, a to formou aserce:</p>
    <code mime="text/x-csrc">
g_assert (g_main_context_is_owner (expected_main_context));</code>

    <p>Když tohle vložíte na začátek každé funkce, selhání aserce zviditelní ty případy, kdy je funkce volána z nesprávného vlákna. Je mnohem jednodušší napsat tyto aserce v rané fázi vývoje kódu, než pak ladit souběh podmínek, který může při volání funkce z nesprávného vlákna snadno nastat.</p>

    <p>Tuto techniku lze použít i u vysílání signálů a zpětných volání, díky čemuž se zvýší typová bezpečnost i kontrola, jestli je použit správný kontext. Upozorňujeme, že vyslání signálu přes <link href="https://developer.gnome.org/gobject/stable/gobject-Signals.html#g-signal-emit"><code>g_signal_emit()</code></link> je asynchronní a vůbec se netýká hlavního kontextu.</p>

    <example>
      <p>Například místo použití následujícího kódu při vyslání signálu:</p>
      <code mime="text/x-csrc" style="invalid">
guint param1;  /* příklad libovolného parametru */
gchar *param2;
guint retval = 0;

g_signal_emit_by_name (my_object, "some-signal",
                       param1, param2, &amp;retval);</code>

      <p>Může být použito následující:</p>
      <code mime="text/x-csrc" style="valid">
static guint
emit_some_signal (GObject     *my_object,
                  guint        param1,
                  const gchar *param2)
{
  guint retval = 0;

  g_assert (g_main_context_is_owner (expected_main_context));

  g_signal_emit_by_name (my_object, "some-signal",
                         param1, param2, &amp;retval);

  return retval;
}</code>
    </example>
  </section>

  <section id="gtask">
    <title><code>GTask</code></title>

    <p><link href="https://developer.gnome.org/gio/stable/GTask.html"><code>GTask</code></link> poskytuje k vyvolání funkcí v jiných vláknech trochu odlišný přístup, který je zaměřen více na případy, kdy by funkce měla být spuštěna v <em>nějakém</em> vlákně na pozadí, ale není určeno, v kterém konkrétním.</p>

    <p><code>GTask</code> přebírá zapouzdřená data a funkci ke spuštění a poskytuje způsob, jak vrátit výsledky z této funkce. Postará se o vše potřebné ke spuštění funkce v libovolném vlákně náležejícímu do stejného svazku vláken sdílených interně v GLib.</p>

    <example>
      <p>Zkombinováním <link xref="#g-main-context-invoke-full"><code>g_main_context_invoke_full()</code></link> a <code>GTask</code> je možné spustit úlohu v konkrétním kontextu a bez většího úsilí vrátit její výsledek do aktuálního kontextu:</p>
      <code mime="text/x-csrc">
/* Toto bude vyvoláno ve vlákně thread1 */
static gboolean
my_func_idle (gpointer user_data)
{
  GTask *task = G_TASK (user_data);
  MyFuncData *data;
  gboolean retval;

  /* Zavolá my_func() a vrácenou pravdivostní hodnotu propaguje
   * do hlavního vlákna */
  data = g_task_get_task_data (task);
  retval = my_func (data-&gt;some_string, data-&gt;some_int,
                    data-&gt;some_object);
  g_task_return_boolean (task, retval);

  return G_SOURCE_REMOVE;
}

/* Ať je toto vyvoláno z kteréhokoliv vlákna, vyvolá se funkce
 * @callback, jakmile je dokončena my_func() a vrátí výsledek */
static void
invoke_my_func_with_result (GMainContext        *thread1_main_context,
                            const gchar         *some_string,
                            guint                some_int,
                            GObject             *some_object,
                            GAsyncReadyCallback  callback,
                            gpointer             user_data)
{
  MyFuncData *data;

  /* Vytvoří zapouzdření dat pro předání všech požadovaných proměnných
   * mezi vlákny */
  data = g_new0 (MyFuncData, 1);
  data-&gt;some_string = g_strdup (some_string);
  data-&gt;some_int = some_int;
  data-&gt;some_object = g_object_ref (some_object);

  /* Vytvoří GTask pro obsluhu vráceného výsledku do hlavního výchozího
   * kontextu aktuálního vlákna */
  task = g_task_new (NULL, NULL, callback, user_data);
  g_task_set_task_data (task, data,
                        (GDestroyNotify) my_func_data_free);

  /* Vyvolá funkci */
  g_main_context_invoke_full (thread1_main_context,
                              G_PRIORITY_DEFAULT, my_func_idle,
                              task,
                              (GDestroyNotify) g_object_unref);
}</code>
    </example>
  </section>
</page>