File: async-programming.page

package info (click to toggle)
gnome-devel-docs 40.3-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 79,188 kB
  • sloc: javascript: 2,514; xml: 2,407; ansic: 2,229; python: 1,854; makefile: 805; sh: 499; cpp: 131
file content (955 lines) | stat: -rw-r--r-- 39,712 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
<?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="async-programming" 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>2015</years>
    </credit>

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

    <desc>Jak použít asynchronní metody ve stylu GLib v různých situacích</desc>
  </info>

  <title>Asynchronní programování</title>

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

    <list>
      <item><p>Dávejte přednost asynchronním voláním před synchronním voláním, nebo výslovným použitím vláken (<link xref="#concepts"/>)</p></item>
      <item><p>Naučte se a dodržujte vzory GLib pro deklarování asynchronních API (<link xref="#api-pattern"/>)</p></item>
      <item><p>Zpětná volání z asynchronních funkcí umístěte v souboru v pořadí jejich provádění, aby byl snadný přehled toku provádění kódu (<link xref="#single-call"/>)</p></item>
      <item><p>Používejte přítomnosti <link href="https://developer.gnome.org/gio/stable/GTask.html"><code>GTask</code></link> nebo <link href="https://developer.gnome.org/gio/stable/GCancellable.html"><code>GCancellable</code></link> k indikaci, jestli operace probíhá (<link xref="#single-call"/>, <link xref="#gtask"/>)</p></item>
      <item><p>Když spouštíte paralelní operace, sledujte kolik jich bylo spuštěno a kolik bylo dokončeno. Po dokončení celé operace by rozdíl obou hodnot měl být nula (<link xref="#parallel"/>)</p></item>
      <item><p>Oddělte stavy pro operace do struktury „data úlohy“ pro objekty <link href="https://developer.gnome.org/gio/stable/GTask.html"><code>GTask</code></link>, aby se operace daly snáze znovu použít bez potřeby změn v globální obsluze stavu (<link xref="#gtask"/>)</p></item>
      <item><p>Popřemýšlejte, jak asynchronní metody v instanci objektu souvisí s finalizací instance (<link xref="#lifetimes"/>)</p></item>
    </list>
  </synopsis>

  <section id="concepts">
    <title>Koncepty</title>

    <p>GLib podporuje <em>asynchronní</em> programování, kdy dlouhotrvající operace mohou být spuštěny, běžet „na pozadí“ a vyvolat zpětné volání, když jsou dokončeny a jejich výsledky jsou k dispozici. Což je v přímém kontrastu se <em>synchronními</em> dlouhotrvajícími operacemi, které jsou samostatnými voláními funkcí blokujícími až do svého dokončení řízení toku programu.</p>

    <p>Jak je vysvětleno v kapitolách <link xref="main-contexts"/> a <link xref="threading#when-to-use-threading"/>, asynchronní operace by měly být upřednostňovány před synchronními, i před cíleným použitím vláken. Na rozdíl od synchronních neblokují hlavní kontext a jejich správné použití je snazší než u vláken. Navíc často mají nižší dopad na výkon, než spouštění vláken.</p>
  </section>

  <section id="api-pattern">
    <title>Vzory API</title>

    <p>Asynchronní volání se řídí standardními vzory v kódu GLib. Pro operaci nazvanou <code>load_data</code> na třídě <code>File</code> ve jmenném prostoru <code>Foo</code> to bude:</p>
    <list>
      <item>
        <code mime="text/x-csrc">
foo_file_load_data_async (FooFile             *self,
                          …,
                          GCancellable        *cancellable,
                          GAsyncReadyCallback  callback,
                          gpointer             user_data)</code>
      </item>
      <item>
        <code mime="text/x-csrc">
foo_file_load_data_finish (FooFile       *self,
                           GAsyncResult  *result,
                           …,
                           GError       **error)</code>
      </item>
    </list>

    <p>Parametry <code>…</code> z <code>foo_file_load_data_async()</code> jsou specifické pro operaci. V tomto případě je o velikost vyrovnávací paměti, do které se načítá. Obdobně u <code>foo_file_load_data_finish()</code> jsou specifické pro operaci návratové hodnoty, v tomto případě se jako typ řetězec vrací umístění.</p>

    <p>Když je zavolána funkce <code>foo_file_load_data_async()</code>, naplánuje operaci načtení na pozadí (jako nový popisovač souboru v <link xref="main-contexts"><code>GMainContext</code></link>, nebo jako pracovní vlákno), a bez blokování se vrátí.</p>

    <p>Když je operace dokončena, je provedeno <code>zpětné volání</code> ve stejném <code>GMainContext</code>, jako původní asynchronní volání. Zpětné volání je zavoláno <em>právě</em> jednou, nezávisle na tom, jestli operace dopadla dobře, nebo selhala.</p>

    <p>Ze zpětného volání může být uživatelským kódem zavolána funkce <code>foo_file_load_data_finish()</code> kvůli získání návratové hodnoty a podrobností o chybě, přičemž se jí předává instance <link href="https://developer.gnome.org/gio/stable/GAsyncResult.html"><code>GAsyncResult</code></link>, která byla předána zpětnému volání.</p>
  </section>

  <section id="lifetimes">
    <title>Životní průběh operace</title>

    <p>Když se píše asynchronní operace, je běžné ji napsat jako metodu třídy. V tomto případě je důležité definovat, jak běžící operace nad instancí třídy komunikují s finalizací instance. Existují dva přístupy:</p>

    <terms>
      <item>
        <title>Silný</title>
        <p>Běžící operace zachovává odkaz na instanci třídy a nutí ji zůstat naživu po dobu operace. Třída může poskytovat nějaký druh metody „close“ nebo „cancel“, které mohou být použity jinou třídou k vynucení zrušení operace a umožnit této instanci finalizaci.</p>
      </item>

      <item>
        <title>Slabý</title>
        <p>Běžící operace <em>ne</em>zachovává odkaz na instanci třídy a třída ruší operaci (pomocí <link href="https://developer.gnome.org/gio/stable/GCancellable.html#g-cancellable-cancel"><code>g_cancellable_cancel()</code></link>) ve své uvolňovací funkci.</p>
      </item>
    </terms>

    <p>To, který přístup je použit, závisí na návrhu třídy. Třída, která obaluje konkrétní operaci (třída <code>MyFileTransfer</code> například), může chtít používat <em style="strong">slabý</em> přístup. Třída, která spravuje více síťových připojení a asynchronních operací nad nimi, může naopak používat <em style="strong">silný</em> přístup. Například kvůli příchozím síťovým připojením nemusí mít úplné řízení nad plánováním svých asynchronních volání, takže slabý přístup by nebyl vhodný – kód zahazující odkaz na objekt by si nemohl být jistý, jestli nešťastnou náhodou nezabije nové síťové připojení.</p>
  </section>

  <section id="async-examples">
    <title>Příklad použití asynchronních funkcí</title>

    <p>Častým případem je, že je k dokončení operace zapotřebí více asynchronních volání. Například, otevření souboru pro čtení, následně provedení několika čtení a pak zavření souboru. Nebo otevření několika síťových soketů souběžně a čekání, dokud se všechny neotevřou, než se může pokračovat s další prací se sítí. Nějaké příklady těchto situací jsou uvedeny níže.</p>

    <section id="single-call">
      <title>Jedna samostatná operace</title>

      <p>Jedna samostatná asynchronní operace vyžaduje dvě funkce: jednu pro spuštění operace a jednu pro její dokončení. V jazyce C žádanou částí provádění asynchronního volání je správně uchovaný stav mezi těmito dvěma funkcemi a obsluha změn tohoto stavu v čase mezi zavoláním těchto dvou funkcí. Například, zrušení probíhajícího asynchronního volání je změna stavu, a když není implementováno pečlivě, aktualizace uživatelského rozhraní (například) provedené při rušení operace budou vráceny do původního stavu aktualizací ve zpětném volání operace.</p>

      <example>
        <p>Tento příklad předvádí kopírování souboru z jednoho místa v souborovém systému na druhé. Předvedené klíčové principy jsou:</p>
        <list>
          <item><p>Umístění funkcí <code>copy_button_clicked_cb()</code> (spuštění) a <code>copy_finish_cb()</code> (dokončení) v postupném pořadí pomocí dopředné deklarace pro <code>copy_finish_cb()</code>. To znamená, že řízení toku pokračuje lineárně od začátku do konce souboru se zdrojovým kódem, místo aby se <code>copy_button_clicked_cb()</code> bralo z konce a pokračovalo se na <code>copy_button_clicked_cb()</code> někde jinde v souboru.</p></item>
          <item><p>Používejte <link href="https://developer.gnome.org/gio/stable/GCancellable.html"><code>GCancellable</code></link>, aby se operace po té, co je spuštěna, dala zrušit. Kód v <code>cancel_button_clicked_cb()</code> je velmi jednoduchý: jelikož je <em>zaručeno</em> vyvolání zpětného volání <code>copy_finish_cb()</code>, když je operace dokončena (i když je dokončena předčasně zrušením), všechny aktualizace uživatelského rozhraní a stavu pro zrušení mohou být obslouženy zde, místo aby se tak stalo v <code>cancel_button_clicked_cb()</code>.</p></item>
          <item><p>Operace běží přesně po dobu, kdy <code>MyObjectPrivate.copy_cancellable</code> není <code>NULL</code>, díky čemuž je jednoduché sledovat běžící operace. Uvědomte si, že to znamená, že jen jedna kopírovací operace může být spuštěna přes <code>copy_button_clicked_cb()</code> v jeden okamžik. Tímto způsobem nejde jednoduše použít jednu <code>GCancellable</code> pro více operací.</p></item>
        </list>

        <code mime="text/x-csrc" style="valid">
static void
copy_finish_cb (GObject      *source_object,
                GAsyncResult *result,
                gpointer      user_data);

static void
copy_button_clicked_cb (GtkButton *button
                        gpointer   user_data)
{
  MyObjectPrivate *priv;
  GFile *source = NULL, *destination = NULL;  /* owned */

  priv = my_object_get_instance_private (MY_OBJECT (user_data));

  /* Operace již běží? */
  if (priv-&gt;copy_cancellable != NULL)
    {
      g_debug ("Copy already in progress.");
      return;
    }

  /* Sestaví cestu k zdrojovému a cílovému souboru */
  source = g_file_new_for_path (/* nějaká cesta vygenerovaná z uživ. rozhraní */);
  destination = g_file_new_for_path (/* nějaká další cesta vygenerovaná z uživ. rozhraní */);

  /* Nastaví jako zrušitelné */
  priv-&gt;copy_cancellable = g_cancellable_new ();

  g_file_copy_async (source, destination, G_FILE_COPY_NONE, G_PRIORITY_DEFAULT,
                     priv-&gt;copy_cancellable, NULL, NULL,
                     copy_finish_cb, user_data);

  g_object_unref (destination);
  g_object_unref (source);

  /* Aktualizuje uživ. rozhraní, aby ukazovalo průběh */
}

static void
copy_finish_cb (GObject      *source_object,
                GAsyncResult *result,
                gpointer      user_data)
{
  MyObjectPrivate *priv;
  GFile *source;  /* unowned */
  GError *error = NULL;

  source = G_FILE (source_object);
  priv = my_object_get_instance_private (MY_OBJECT (user_data));

  /* Obsluha dokončení operace */
  g_file_copy_finish (source, result, &amp;error);

  if (error != NULL &amp;&amp;
      !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
    {
      /* Mělo by aktualizovat uživatelské rozhraní, aby oznámilo selhání
       * Ignoruje selhání kvůli zrušení */
      g_warning ("Failed to copy file: %s", error-&gt;message);
    }

  g_clear_error (&amp;error);

  /* Smaže příznak zrušitelnosti, aby dalo najevo dokončení operace */
  g_clear_object (&amp;priv-&gt;copy_cancellable);

  /* Aktualizace uživ. rozhraní, aby ukazovalo dokončení kopírování */
}

static void
cancel_button_clicked_cb (GtkButton *button,
                          gpointer   user_data)
{
  MyObjectPrivate *priv;
  GFile *source = NULL, *destination = NULL;  /* owned */

  priv = my_object_get_instance_private (MY_OBJECT (user_data));

  /* Operace probíhá? Žádná operace, když @copy_cancellable je %NULL. */
  g_cancellable_cancel (priv-&gt;copy_cancellable);
}

static void
my_object_dispose (GObject *obj)
{
  MyObjectPrivate *priv;

  priv = my_object_get_instance_private (MY_OBJECT (obj));

  /* Zrušení případné probíhající operace kopírování.
   *
   * Tím se zajistí, že když je #MyObject částečně uvolněn přes
   * kopírování, není zpětné volání vyvoláno s neplatným
   * ukazatelem na #MyObject. */\n"
  g_cancellable_cancel (priv-&gt;copy_cancellable);

  /* Zde proveďte případná další uvolnění */

  /* Zřetězení */
  G_OBJECT_CLASS (my_object_parent_class)-&gt;dispose (obj);
}</code>

        <p>Pro srovnání, zde je ten stejný kód implementovaný pomocí <em>synchronní</em> verze <link href="https://developer.gnome.org/gio/stable/GFile.html#g-file-copy"><code>g_file_copy()</code></link>. Všimněte si, že pořadí příkazů je povětšinou stejné. Rušení v tomto případě nelez podporovat, protože uživatelské rozhraní je během kopírování blokované od příjmu události „click“ z tlačítka „Zrušit“, takže do parametru <code>GCancellable</code> předáváme <code>NULL</code>. To je také hlavní důvod, proč tento kód <em>není</em> v praxi použitelný.</p>

        <code mime="text/x-csrc" style="invalid">
static void
copy_button_clicked_cb (GtkButton *button
                        gpointer   user_data)
{
  MyObjectPrivate *priv;
  GFile *source = NULL, *destination = NULL;  /* owned */

  priv = my_object_get_instance_private (MY_OBJECT (user_data));

  /* Sestaví cestu k zdrojovému a cílovému souboru */
  source = g_file_new_for_path (/* nějaká cesta vygenerovaná z uživ. rozhraní */);
  destination = g_file_new_for_path (/* nějaká další cesta vygenerovaná z uživ. rozhraní */);

  g_file_copy (source, destination, G_FILE_COPY_NONE,
               NULL  /* cancellable */, NULL, NULL,
               &amp;error);

  g_object_unref (destination);
  g_object_unref (source);

  /* Obsluha dokončení operace */
  if (error != NULL)
    {
      /* Mělo by aktualizovat uživatelské rozhraní, aby oznámilo selhání
       * Ignoruje selhání kvůli zrušení */
      g_warning ("Failed to copy file: %s", error-&gt;message);
    }

  g_clear_error (&amp;error);

  /* Aktualizace uživ. rozhraní, aby ukazovalo dokončení kopírování */
}</code>
      </example>
    </section>

    <section id="series">
      <title>Operace prováděné sériově</title>

      <p>Běžnou situací je spuštění více asynchronních operací po sobě, když každá z operací závisí na dokončení předchozí.</p>

      <example>
        <p>V tomto příkladu aplikace čte adresu soketu ze souboru, otevře připojení na tuto adresu, přečte zprávu a pak skončí.</p>

        <p>Klíčové body v tomto příkladu:</p>
        <list>
          <item><p>Jednotlivá zpětná volání jsou jednotně očíslovaná a ve správném pořadí umístěná v souboru se zdrojovým kódem, takže kód je provádět postupně ve stejném pořadí.</p></item>
          <item><p>Stejně jako v kapitole <link xref="#single-call"/>, jediné <code>GCancellable</code> indikuje, že běží série operací. Její zrušení přeruší celou posloupnost.</p></item>
          <item><p>Stejně jako v <link xref="#single-call"/> jsou čekající operace zrušeny, když je vlastnící instance <code>MyObject</code> uvolněna, aby se zabránilo pozdějšímu zavolání zpětného volání s neplatným ukazatelem <code>MyObject</code>.</p></item>
        </list>

        <p><link xref="#gtask"/> poskytuje verzi tohoto příkladu obalenou pro pohodlí do <link href="https://developer.gnome.org/gio/stable/GTask.html"><code>GTask</code></link>.</p>

        <code mime="text/x-csrc" style="valid">
static void
connect_to_server_cb1 (GObject      *source_object,
                       GAsyncResult *result,
                       gpointer      user_data);
static void
connect_to_server_cb2 (GObject      *source_object,
                       GAsyncResult *result,
                       gpointer      user_data);
static void
connect_to_server_cb3 (GObject      *source_object,
                       GAsyncResult *result,
                       gpointer      user_data);

static void
connect_to_server (MyObject *self)
{
  MyObjectPrivate *priv;
  GFile *address_file = NULL;  /* owned */

  priv = my_object_get_instance_private (self);

  if (priv-&gt;connect_cancellable != NULL)
    {
      /* Již se připojuje */
      return;
    }

  /* Nastavení jako zrušitelné */
  priv-&gt;connect_cancellable = g_cancellable_new ();

  /* Čtení adresy soketu */
  address_file = build_address_file ();
  g_file_load_contents_async (address_file, priv-&gt;connect_cancellable,
                              connect_to_server_cb1, self);
  g_object_unref (address_file);
}

static void
connect_to_server_cb1 (GObject      *source_object,
                       GAsyncResult *result,
                       gpointer      user_data)
{
  MyObject *self;
  MyObjectPrivate *priv;
  GFile *address_file;  /* unowned */
  gchar *address = NULL;  /* owned */
  gsize address_size = 0;
  GInetAddress *inet_address = NULL;  /* owned */
  GInetSocketAddress *inet_socket_address = NULL;  /* owned */
  guint16 port = 123;
  GSocketClient *socket_client = NULL;  /* owned */
  GError *error = NULL;

  address_file = G_FILE (source_object);
  self = MY_OBJECT (user_data);
  priv = my_object_get_instance_private (self);

  /* Dokončení načítání adresy */
  g_file_load_contents_finish (address_file, result, &amp;address,
                               &amp;address_size, NULL, &amp;error);

  if (error != NULL)
    {
      goto done;
    }

  /* Zpracování adresy */
  inet_address = g_inet_address_new_from_string (address);

  if (inet_address == NULL)
    {
      /* Chyba */
      g_set_error (&amp;error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
                   "Invalid address ‘%s’.", address);
      goto done;
    }

  inet_socket_address = g_inet_socket_address_new (inet_address, port);

  /* Připojení na zadanou adresu */
  socket_client = g_socket_client_new ();

  g_socket_client_connect_async (socket_client,
                                 G_SOCKET_CONNECTABLE (inet_socket_address),
                                 priv-&gt;connect_cancellable,
                                 connect_to_server_cb2,
                                 self);

done:
  if (error != NULL)
    {
      /* Zastavení operace */
      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
        {
          g_warning ("Failed to load server address: %s", error-&gt;message);
        }

      g_clear_object (&amp;priv-&gt;connect_cancellable);
      g_error_free (error);
    }

  g_free (address);
  g_clear_object (&amp;inet_address);
  g_clear_object (&amp;inet_socket_address);
  g_clear_object (&amp;socket_client);
}

static void
connect_to_server_cb2 (GObject      *source_object,
                       GAsyncResult *result,
                       gpointer      user_data)
{
  MyObject *self;
  MyObjectPrivate *priv;
  GSocketClient *socket_client;  /* unowned */
  GSocketConnection *connection = NULL;  /* owned */
  GInputStream *input_stream;  /* unowned */
  GError *error = NULL;

  socket_client = G_SOCKET_CLIENT (source_object);
  self = MY_OBJECT (user_data);
  priv = my_object_get_instance_private (self);

  /* Dokončení připojování k soketu */
  connection = g_socket_client_connect_finish (socket_client, result,
                                               &amp;error);

  if (error != NULL)
    {
      goto done;
    }

  /* Uchování odkazu na připojení, takže je udržované otevřené po dobu co z něj
   * čteme: #GInputStream neudržuje odkaz na #GIOStream, který jej obsahuje */
  priv-&gt;connection = g_object_ref (connection);

  /* Čtení zprávy z připojení. Používá jedinou vyrovnávací paměť uchovanou v
   * #MyObject, což znamená, že v danou chvíli může běžet jen jediná operace
   * connect_to_server(). Pokud je to problém , může být vyrovnávací paměť
   * místo toho alokována dynamicky. */
  input_stream = g_io_stream_get_input_stream (G_IO_STREAM (connection));

  g_input_stream_read_async (input_stream,
                             priv-&gt;message_buffer,
                             sizeof (priv-&gt;message_buffer),
                             G_PRIORITY_DEFAULT, priv-&gt;connect_cancellable,
                             connect_to_server_cb3, self);

done:
  if (error != NULL)
    {
      /* Zastavení operace */
      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
        {
          g_warning ("Failed to connect to server: %s", error-&gt;message);
        }

      g_clear_object (&amp;priv-&gt;connect_cancellable);
      g_clear_object (&amp;priv-&gt;connection);
      g_error_free (error);
    }

  g_clear_object (&amp;connection);
}

static void
connect_to_server_cb3 (GObject      *source_object,
                       GAsyncResult *result,
                       gpointer      user_data)
{
  MyObject *self;
  MyObjectPrivate *priv;
  GInputStream *input_stream;  /* unowned */
  gssize len = 0;
  GError *error = NULL;

  input_stream = G_INPUT_STREAM (source_object);
  self = MY_OBJECT (user_data);
  priv = my_object_get_instance_private (self);

  /* Dokončení čtení ze soketu */
  len = g_input_stream_read_finish (input_stream, result, &amp;error);

  if (error != NULL)
    {
      goto done;
    }

  /* Obsluha zprávy */
  g_assert_cmpint (len, &gt;=, 0);
  g_assert_cmpuint ((gsize) len, &lt;=, sizeof (priv-&gt;message_buffer));

  handle_received_message (self, priv-&gt;message_buffer, len, &amp;error);

  if (error != NULL)
    {
      goto done;
    }

done:
  /* Nepodmíněné označení operace za dokončenou.
   *
   * Proud dat by se měl automaticky zavřít, protože
   * je zahozen poslední odkaz. */
  g_clear_object (&amp;priv-&gt;connect_cancellable);
  g_clear_object (&amp;priv-&gt;connection);

  if (error != NULL)
    {
      /* Varování o chybě */
      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
        {
          g_warning ("Failed to read from the server: %s", error-&gt;message);
        }

      g_error_free (error);
    }
}

static void
my_object_dispose (GObject *obj)
{
  MyObjectPrivate *priv;

  priv = my_object_get_instance_private (MY_OBJECT (obj));

  /* Zrušení případných probíhajících operací mazání.
   *
   * Tím se zajistí, že když je #MyObject částečně uvolněn přes
   * sadu operací delete_files(), sada obdrží zrušení a nepokračuje
   * s neplatným ukazatelem na #MyObject. */
  g_cancellable_cancel (priv-&gt;connect_cancellable);

  /* Zde proveďte případná další uvolnění */

  /* Zřetězení */
  G_OBJECT_CLASS (my_object_parent_class)-&gt;dispose (obj);
}</code>
      </example>
    </section>

    <section id="parallel">
      <title>Operace prováděné paralelně</title>

      <p>Jinou běžnou situací je spustit více asynchronních operací souběžně s předpokladem, že celková operace bude dokončena, jakmile všechny její součásti doběhnou.</p>

      <example>
        <p>V tomto příkladu aplikace maže souběžně několik souborů.</p>

        <p>Klíčové body v tomto příkladu:</p>
        <list>
          <item><p>Počet čekajících asynchronních operací (tj. těch, které byly spuštěny a ještě nedoběhly) je sledován v <code>n_deletions_pending</code>. Zpětné volání <code>delete_files_cb()</code> bere celkovou operaci za dokončenou, až když tato hodnota dosáhne nuly.</p></item>
          <item><p><code>n_deletions_to_start</code> sleduje spouštění operací mazání, v případě <link href="https://developer.gnome.org/gio/stable/GFile.html#g-file-delete-async"><code>g_file_delete_async()</code></link> spravuje použití rychlé cesty a dokončení synchronně (bez blokování).</p></item>
          <item><p>Stejně jako v <link xref="#single-call"/>, jsou všechna čekající mazání zrušena v situaci, kdy je vlastnící instance <code>MyObject</code> uvolněna, aby se zabránilo pozdějšímu zavolání zpětného volání s neplatným ukazatelem <code>MyObject</code>.</p></item>
        </list>

        <code mime="text/x-csrc" style="valid">
static void
delete_files_cb (GObject      *source_object,
                 GAsyncResult *result,
                 gpointer      user_data);

static void
delete_files (MyObject *self,
              GPtrArray/*&lt;owned GFile*&gt;&gt;*/ *files)
{
  MyObjectPrivate *priv;
  GFile *address_file = NULL;  /* owned */

  priv = my_object_get_instance_private (self);

  /* Nastavení jako zrušitelné, pokud již neběží žádná operace */
  if (priv-&gt;delete_cancellable == NULL)
    {
      priv-&gt;delete_cancellable = g_cancellable_new ();
      priv-&gt;n_deletions_pending = 0;
      priv-&gt;n_deletions_total = 0;
    }

  /* Aktualizace interního stavu a dočasné nastavení @n_deletions_to_start.
   * To se používá v delete_files_cb(), aby se předešlo oznámení, že je celá
   * operace dokončená, zatím co ona stále běží. To by se mohlo stát, když
   * g_file_delete_async() doběhne synchronně, například, když existuje
   * neblokující rychlá cesta pro zadaný souborový systém. */
  priv-&gt;n_deletions_pending += files-&gt;len;
  priv-&gt;n_deletions_total += files-&gt;len;
  priv-&gt;n_deletions_to_start = files-&gt;len;

  /* Aktulazice uživatelského rozhraní, aby dalo najevo, že je soubor mazán */
  update_ui_to_show_progress (self,
                              priv-&gt;n_deletions_pending,
                              priv-&gt;n_deletions_total);

  /* Začne souběžně všechny operace mazání. Sdílejí stejný #GCancellable. */
  for (i = 0; i &lt; files-&gt;len; i++)
    {
      GFile *file = files-&gt;pdata[i];

      priv-&gt;n_deletions_to_start--;
      g_file_delete_async (file, G_PRIORITY_DEFAULT, priv-&gt;delete_cancellable,
                           delete_files_cb, self);
    }
}

static void
delete_files_cb (GObject      *source_object,
                 GAsyncResult *result,
                 gpointer      user_data)
{
  MyObject *self;
  MyObjectPrivate *priv;
  GFile *file;  /* unowned */
  GError *error = NULL;

  file = G_FILE (source_object);
  self = MY_OBJECT (user_data);
  priv = my_object_get_instance_private (self);

  /* Dokončení mazání souboru */
  g_file_delete_finish (file, result, &amp;error);

  if (error != NULL &amp;&amp;
      !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
    {
      g_warning ("Error deleting file: %s", error-&gt;message);
    }

  g_clear_error (&amp;error);

  /* Aktualizace interního stavu */
  g_assert_cmpuint (priv-&gt;n_deletions_pending, &gt;, 0);
  priv-&gt;n_deletions_pending--;

  /* Aktualizace uživatelského rozhraní, aby ukazovalo průběh */
  update_ui_to_show_progress (self,
                              priv-&gt;n_deletions_pending,
                              priv-&gt;n_deletions_total);

  /* Jestliže je všechno mazání dokončeno a žádné další nezačíná, aktualizuje se,
   * uživatelské rozhraní, aby ukazovalo, že je vše dokončeno. */
  if (priv-&gt;n_deletions_pending == 0 &amp;&amp; priv-&gt;n_deletions_to_start == 0)
    {
      update_ui_to_show_completion (self);

      /* Vyčištění stavu operace */
      g_clear_object (&amp;priv-&gt;delete_cancellable);
      priv-&gt;n_deletions_total = 0;
    }
}

static void
my_object_dispose (GObject *obj)
{
  MyObjectPrivate *priv;

  priv = my_object_get_instance_private (MY_OBJECT (obj));

  /* Zrušení případných probíhajících operací mazání.
   *
   * Tím se zajistí, že když je #MyObject částečně uvolněn přes
   * sadu operací delete_files(), sada obdrží zrušení a nepokračuje
   * s neplatným ukazatelem na #MyObject. */
  g_cancellable_cancel (priv-&gt;delete_cancellable);

  /* Zde proveďte případná další uvolnění */

  /* Zřetězení */
  G_OBJECT_CLASS (my_object_parent_class)-&gt;dispose (obj);
}</code>
      </example>
    </section>

    <section id="gtask">
      <title>Obalení pomocí <code>GTask</code></title>

      <p>Asynchronní operace (nebo sady operací) často, když se stanou více komplexními, potřebují přiřazený stav. Ten bývá typicky uchován ve vlastní struktuře, ale definování nové struktury pro uchování standardního zpětného volání, uživatelských dat a n-tice zrušitelných je pracné. <link href="https://developer.gnome.org/gio/stable/GTask.html"><code>GTask</code></link> to zjednodušuje poskytnutím standardního způsobu obalení všech tří věcí, plus navíc vlastními „daty úlohy“.</p>

      <p>Použití <code>GTask</code> může nahradit použití <link href="https://developer.gnome.org/gio/stable/GCancellable.html"><code>GCancellable</code></link> pro indikaci, jestli běží nějaká operace.</p>

      <example>
        <p>Tento příklad se z hlediska funkcionality shoduje s <link xref="#series"/>, ale je přepracován tak, aby používal <code>GTask</code> k obalení posloupnosti operací.</p>

        <p>Klíčové body v tomto příkladu:</p>
        <list>
          <item><p>Stav, který byl v <link xref="#series"/> v <code>MyObjectPrivate</code>, je nyní uzavřen v <code>ConnectToServerData</code>, který je nastaven jako „daty úlohy“ objektu <code>GTask</code> představujícího celkovou operaci. To znamená, že je automaticky uvolněn po návratu z operace.</p></item>
          <item><p>Navíc to znamená, že zacházení se stavem <code>MyObjectPrivate</code> je omezeno na začátek a konec posloupnosti operací, takže znovupoužití úlohy v jiné situaci se stává snazší. Například je nyní mnohem snazší podporovat běh více takovýchto úloh souběžně.</p></item>
          <item><p>Protože <code>GTask</code> udržuje odkaz na <code>MyObject</code>, není objekt možné uvolnit, když probíhá posloupnost operací. Proto byl kód <code>my_object_dispose()</code> odstraněn. Místo něj je teď metoda <code>my_object_close()</code>, která umožňuje zrušení případných čekajících operací, takže <code>MyObject</code> může být v případě potřeby uvolněn.</p></item>
        </list>

        <code mime="text/x-csrc" style="valid">
static void
connect_to_server_cb1 (GObject      *source_object,
                       GAsyncResult *result,
                       gpointer      user_data);
static void
connect_to_server_cb2 (GObject      *source_object,
                       GAsyncResult *result,
                       gpointer      user_data);
static void
connect_to_server_cb3 (GObject      *source_object,
                       GAsyncResult *result,
                       gpointer      user_data);

typedef struct {
  GSocketConnection *connection;  /* nullable; owned */
  guint8 message_buffer[128];
} ConnectToServerData;

static void
connect_to_server_data_free (ConnectToServerData *data)
{
  g_clear_object (&amp;data-&gt;connection);
}

void
my_object_connect_to_server_async (MyObject            *self,
                                   GCancellable        *cancellable,
                                   GAsyncReadyCallback  callback,
                                   gpointer             user_data)
{
  MyObjectPrivate *priv;
  GTask *task = NULL;  /* owned */
  ConnectToServerData *data = NULL;  /* owned */
  GFile *address_file = NULL;  /* owned */

  g_return_if_fail (MY_IS_OBJECT (self));
  g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));

  priv = my_object_get_instance_private (self);

  if (priv-&gt;connect_task != NULL)
    {
      g_task_report_new_error (self, callback, user_data, NULL,
                               G_IO_ERROR, G_IO_ERROR_PENDING,
                               "Already connecting to the server.");
      return;
    }

  /* Nastavení, že je zrušitelné */
  if (cancellable != NULL)
    {
      g_object_ref (cancellable);
    }
  else
    {
      cancellable = g_cancellable_new ();
    }

  /* Nastavení úlohy */
  task = g_task_new (self, cancellable, callback, user_data);
  g_task_set_check_cancellable (task, FALSE);

  data = g_malloc0 (sizeof (ConnectToServerData));
  g_task_set_task_data (task, data,
                        (GDestroyNotify) connect_to_server_data_free);

  g_object_unref (cancellable);

  priv-&gt;connect_task = g_object_ref (task);

  /* Čtení adresy soketu */
  address_file = build_address_file ();
  g_file_load_contents_async (address_file, g_task_get_cancellable (task),
                              connect_to_server_cb1, g_object_ref (task));
  g_object_unref (address_file);

  g_clear_object (&amp;task);
}

static void
connect_to_server_cb1 (GObject      *source_object,
                       GAsyncResult *result,
                       gpointer      user_data)
{
  MyObject *self;
  MyObjectPrivate *priv;
  GTask *task = NULL;  /* owned */
  GFile *address_file;  /* unowned */
  gchar *address = NULL;  /* owned */
  gsize address_size = 0;
  GInetAddress *inet_address = NULL;  /* owned */
  GInetSocketAddress *inet_socket_address = NULL;  /* owned */
  guint16 port = 123;
  GSocketClient *socket_client = NULL;  /* owned */
  GError *error = NULL;

  address_file = G_FILE (source_object);
  task = G_TASK (user_data);
  self = g_task_get_source_object (task);
  priv = my_object_get_instance_private (self);

  /* Dokončení načtení adresy */
  g_file_load_contents_finish (address_file, result, &amp;address,
                               &amp;address_size, NULL, &amp;error);

  if (error != NULL)
    {
      goto done;
    }

  /* Zpracování adresy */
  inet_address = g_inet_address_new_from_string (address);

  if (inet_address == NULL)
    {
      /* Chyba */
      g_set_error (&amp;error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
                   "Invalid address ‘%s’.", address);
      goto done;
    }

  inet_socket_address = g_inet_socket_address_new (inet_address, port);

  /* Připojení k zadané adrese */
  socket_client = g_socket_client_new ();

  g_socket_client_connect_async (socket_client,
                                 G_SOCKET_CONNECTABLE (inet_socket_address),
                                 g_task_get_cancellable (task),
                                 connect_to_server_cb2,
                                 g_object_ref (task));

done:
  if (error != NULL)
    {
      /* Zastavení operace a propagace chyby */
      g_clear_object (&amp;priv-&gt;connect_task);
      g_task_return_error (task, error);
    }

  g_free (address);
  g_clear_object (&amp;inet_address);
  g_clear_object (&amp;inet_socket_address);
  g_clear_object (&amp;socket_client);
  g_clear_object (&amp;task);
}

static void
connect_to_server_cb2 (GObject      *source_object,
                       GAsyncResult *result,
                       gpointer      user_data)
{
  MyObject *self;
  MyObjectPrivate *priv;
  GTask *task = NULL;  /* owned */
  ConnectToServerData *data;  /* unowned */
  GSocketClient *socket_client;  /* unowned */
  GSocketConnection *connection = NULL;  /* owned */
  GInputStream *input_stream;  /* unowned */
  GError *error = NULL;

  socket_client = G_SOCKET_CLIENT (source_object);
  task = G_TASK (user_data);
  data = g_task_get_task_data (task);
  self = g_task_get_source_object (task);
  priv = my_object_get_instance_private (self);

  /* Dokončení připojení k soketu */
  connection = g_socket_client_connect_finish (socket_client, result,
                                               &amp;error);

  if (error != NULL)
    {
      goto done;
    }

  /* Uchování odkazu na připojení, takže je udržované otevřené po dobu co z něj
   * čteme: #GInputStream neudržuje odkaz na #GIOStream, který jej obsahuje */
  data-&gt;connection = g_object_ref (connection);

  /* Čtení zprávy z připojení. Protože je vyrovnávací paměť alokovaná jako
   * součást @data pro jednotlivé úlohy, může běžet víc úloh naráz. */
  input_stream = g_io_stream_get_input_stream (G_IO_STREAM (connection));

  g_input_stream_read_async (input_stream,
                             data-&gt;message_buffer,
                             sizeof (data-&gt;message_buffer),
                             G_PRIORITY_DEFAULT, g_task_get_cancellable (task),
                             connect_to_server_cb3, g_object_ref (task));

done:
  if (error != NULL)
    {
      /* Zastavení operace a propagace chyby */
      g_clear_object (&amp;priv-&gt;connect_task);
      g_task_return_error (task, error);
    }

  g_clear_object (&amp;connection);
  g_clear_object (&amp;task);
}

static void
connect_to_server_cb3 (GObject      *source_object,
                       GAsyncResult *result,
                       gpointer      user_data)
{
  MyObject *self;
  MyObjectPrivate *priv;
  GTask *task = NULL;  /* owned */
  ConnectToServerData *data;  /* unowned */
  GInputStream *input_stream;  /* unowned */
  gssize len = 0;
  GError *error = NULL;

  input_stream = G_INPUT_STREAM (source_object);
  task = G_TASK (user_data);
  data = g_task_get_task_data (task);
  self = g_task_get_source_object (task);
  priv = my_object_get_instance_private (self);

  /* Dokončení čtení ze soketu */
  len = g_input_stream_read_finish (input_stream, result, &amp;error);

  if (error != NULL)
    {
      goto done;
    }

  /* Obsluha zprávy */
  g_assert_cmpint (len, &gt;=, 0);
  g_assert_cmpuint ((gsize) len, &lt;=, sizeof (data-&gt;message_buffer));

  handle_received_message (self, data-&gt;message_buffer, len, &amp;error);

  if (error != NULL)
    {
      goto done;
    }

  /* Úspěch! */
  g_task_return_boolean (task, TRUE);

done:
  /* Nepodmíněné označení operace za dokončenou.
   *
   * Proud dat by se měl automaticky zavřít, protože
   * je zahozen poslední odkaz. */
  g_clear_object (&amp;priv-&gt;connect_task);

  if (error != NULL)
    {
      /* Zastavení operace a propagace chyby */
      g_task_return_error (task, error);
    }

  g_clear_object (&amp;task);
}

void
my_object_connect_to_server_finish (MyObject      *self,
                                    GAsyncResult  *result,
                                    GError       **error)
{
  g_return_if_fail (MY_IS_OBJECT (self));
  g_return_if_fail (g_task_is_valid (result, self));
  g_return_if_fail (error == NULL || *error == NULL);

  g_task_propagate_boolean (G_TASK (result), error);
}

void
my_object_close (MyObject *self)
{
  MyObjectPrivate *priv;

  g_return_if_fail (MY_IS_OBJECT (self));

  priv = my_object_get_instance_private (self);

  if (priv-&gt;connect_task != NULL)
    {
      GCancellable *cancellable = g_task_get_cancellable (priv-&gt;connect_task);
      g_cancellable_cancel (cancellable);
    }
}</code>
      </example>
    </section>
  </section>
</page>