Soru Sevk kuyrukları: Çalışıyor ve nasıl durduracaklarını nasıl anlarız?


Sadece GCD ile uğraşıyorum ve bir oyuncak CoinFlipper uygulaması yazdım.

İşte paraları çeviren yöntem:

- (void)flipCoins:(NSUInteger)nFlips{

    // Create the queues for work
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL);

    // Split the number of flips into whole chunks of kChunkSize and the remainder.
    NSUInteger numberOfWholeChunks = nFlips / kChunkSize;
    NSUInteger numberOfRemainingFlips = nFlips - numberOfWholeChunks * kChunkSize;

    if (numberOfWholeChunks > 0) {
        for (NSUInteger index = 0; index < numberOfWholeChunks; index++) {
            dispatch_async(queue, ^{
                NSUInteger h = 0;
                NSUInteger t = 0;
                flipTheCoins(kChunkSize, &h, &t);
                dispatch_async(mainQueue, ^{
                    self.nHeads += h;
                    self.nTails += t;
                });
            });
        }
    }
    if (numberOfRemainingFlips > 0) {
        dispatch_async(queue, ^{
            NSUInteger h = 0;
            NSUInteger t = 0;
            flipTheCoins(numberOfRemainingFlips, &h, &t);
            dispatch_async(mainQueue, ^{
                self.nHeads += h;
                self.nTails += t;
            });
        });

    }
}

Gördüğün gibi; Sayışların sayısını arka planda ters çeviren ve ana sıradaki özellikleri güncelleyen büyük parçalara bölüyorum. Özellikler, pencere denetleyicisi tarafından izlenmekte ve bir UI, çalışan sonuçlarla güncellenmektedir.

Eşzamanlılık Programlama Kılavuzu ve GCD belgelerini inceledim ve bir kuyruğu askıya almanın bir yolu olmasına rağmen, onları durdurmanın ve tüm sıraya alınmış ve çalıştırılmamış nesnelerin kaldırılmasının bir yolu yok.

Başladıktan sonra saygısızlığı iptal etmek için bir 'durdur' düğmesine bağlanmayı çok isterim. İle NSOperationQueue Gözlemleyebilirim operationCount Çalışıyor olup olmadığını bilmek için cancelAllOperations kuyruklu blokları kaldırmak için

Eşzamanlılık Programlama Kılavuzu ve GCD belgelerini inceledim ve bir kuyruğu askıya almanın bir yolu olmasına rağmen, onları durdurmanın ve tüm sıraya alınmış ve çalıştırılmamış nesnelerin kaldırılmasının bir yolu yok.

Yani :-

  1. Kuyrukta eklediğim blokların hala bekleyip beklemediğini nasıl anlarım?
  2. Henüz çalışmadığınız blokları nasıl iptal edebilirim?
  3. Ben GCD şeylerine yeniyim, bu yüzden doğru mu yapıyorum?

32
2017-10-11 13:12


Menşei




Cevaplar:


Bu, GCD ile programlama yaparken yarı ortak bir sorudur.

Kısa cevap, GCD'nin kuyruklar için bir iptal API'sine sahip olmamasıdır. Gerekçe:

  1. bellek yönetimi çok daha karmaşık hale gelecektir, çünkü belirli bir blok, belirli bir bellek tahsisinden sorumlu olabilir. Her zaman bloğu çalıştırarak GCD, bellek yönetiminin kolay olmasını sağlar.
  2. Durdurmak için pratik olarak imkansız. koşu devleti bozmadan bloklamak.
  3. İptal mantığına ihtiyaç duyan çoğu kod, bu durumu zaten özel veri yapılarında izlemektedir.

Tüm bu durumlar göz önüne alındığında, böyle bir kod yazmak çok daha etkili ve güçlüdür:

dispatch_async(my_obj->queue, ^{
    bool done = false;
    // do_full_update() takes too long, therefore:
    while ( !my_obj->cancelled && !done ) {
        done = do_partial_update(my_obj);
    }
});

Oh, ve sıraya alınmış tüm blokları çalıştıran bir sıra bittiğini bilmek için, kodunuz sadece boş bir bloğu senkron API:

dispatch_sync(my_obj->queue, ^{});

Yorumlarda belirtildiği gibi, işinizin ne zaman yapıldığını bilmenin daha iyi bir yolu, gönderim gruplarını kullanmaktır. Tüm bloklarınızı gruba gönderin ve daha sonra gruba bir tamamlama işleyicisi ekleyebilirsiniz. İş tamamlandığında, tamamlama bloğu çalışır.

dispatch_group_t myGroup = dispatch_group_create();
dispatch_group_async(myGroup, my_obj->queue, ^{
    bool done = false;
    while ( !my_obj->cancelled && !done ) {
        done = do_partial_update(my_obj);
    }
});
dispatch_group_notify(myGroup, my_obj->queue, ^{
    NSLog(@"Work is done!");
    dispatch_release(myGroup);
});

Tüm bloklarınız tamamlandığında, grup boş olacak ve bildirim bloğunu tetikleyecektir. Buradan, UI'yi güncelleyebilirsiniz.

İyi şanslar ve iyi eğlenceler!


47
2017-10-11 16:15



Veya eşzamanlı olarak göndermek yerine, yeni bir dispatch_group oluşturabilir, tüm bloklarınızı eşzamansız olarak aynı gruba gönderebilir ve ardından grubun bitmesini bekleyebilirsiniz. Sanırım bu toplam satır sayısına 3 satır ekliyor (1 grup oluşturmak için, 1 beklemek için 1 ve grubu yok etmek için 1). - Dave DeLong
yapar dispatch_sync(my_obj->queue, ^{}); bir değer mi döndürmek Sıranız bittiğinde veya devam etmediğinde bu yürütmeden nasıl haberdar olabilirsiniz? - johnbakers
Sebby-- dispatch_sync(my_obj->queue, ^{}); döner ne zaman sıra tüm blokları çalıştı - Ilias Karim
Veya kullanmak NSOperationQueue yerine. - adib
nedir my_obj? - user924


Çalışıyorsa nasıl söylenir

BOOL dispatch_queue_is_empty(dispatch_queue_t queue)
{
    dispatch_group_t group = dispatch_group_create();

    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        dispatch_group_leave(group);
    });

    int64_t maxWaitTime = 0.00000005 * NSEC_PER_SEC;
    BOOL isReady = dispatch_group_wait(group, maxWaitTime) == 0;

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
        dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
        dispatch_release(group);
    });

    return isReady;
}

Uygulamada test etmek için

dispatch_queue_t queue = dispatch_queue_create("test", 0);

NSLog(@"Is empty %@", dispatch_queue_is_empty(queue) ? @"YES" : @"NO");

dispatch_async(queue, ^{
    for(int i = 0; i < 100; i++)
    {
        NSLog(@"... %i", i);
    }
});

NSLog(@"Is empty %@", dispatch_queue_is_empty(queue) ? @"YES" : @"NO");

Sonuç

Is empty YES
Is empty NO
... 0
... 1
... 2
... 3
... 4
... 5
... 6
... 7
... 8
... 9

Değişken için varsayılan değer maxWaitTime istenilen sonuca getirilebilir.


6
2017-10-14 17:31



benim için çalışmayan alway 0 döndürür - kokemomuke
döner 0 hangi işlevden Demek istiyorsun false? Hiçbir işlev döndürmez 0 - hfossli
#hfossli sadece kopyalayıp kodunuzu kopyalayın, ve bu şu şekilde gösterilir: Boş mu Boş mu Boş 0 0 2 3 4 5 - kokemomuke


Seri gönderi kuyruğunuz veya bir eşzamanlı gönderim sıranız varsa, aynı şeyi yapabilecek bir kod.

BOOL __block queueIsEmpty = false;
dispatch_barrier_async (_dispatchQueue, ^{
    queueIsEmpty = true;
});

while (!queueIsEmpty) {
    int i = 0;  // NOOP instruction
}

// At this point your queue should be empty.

1
2017-07-17 22:02