Soru Bir nesnenin birden fazla iş parçacığı tarafından erişilmesini engelleme - Objective-C


Objective-C'de iplik güvenliği ile ilgili bir sorum var. Bazı diğer cevapları, bazı Apple belgelerini okudum ve bununla ilgili bazı şüphelerim var, bu yüzden kendi sorumu sormayı düşündüm.

Sorum şu üç kat:

Sanırım bir dizim var. NSMutableArray *myAwesomeArray;

1. kat: 

Şimdi yanılıyorsam beni düzelt, ama anladığım kadarıyla, kullanarak @synchronized(myAwesomeArray){...} iki iş parçacığının aynı kod bloğuna erişmesini engeller. Yani, temelde, şöyle bir şey varsa:

-(void)doSomething {
    @synchronized(myAwesomeArray) {
        //some read/write operation on myAwesomeArray
    }
}

Daha sonra, iki iş parçacığı erişirse aynı yöntemde aynı Zaman, bu kod bloğu iş parçacığı güvenli olacak. Sanırım bu kısmı doğru anladım.

Kat 2:

Ne yapmalıyım myAwesomeArray farklı yöntemlerden birden çok iş parçacığı tarafından erişiliyor? Benim gibi bir şeyim varsa:

- (void)readFromArrayAccessedByThreadOne {
    //thread 1 reads from myAwesomeArray
}

- (void)writeToArrayAccessedByThreadTwo {
    //thread 2 writes to myAwesomeArray
}

Şimdi, her iki yönteme aynı anda iki farklı iplik tarafından erişilir. Bunu nasıl temin ederim? myAwesomeArray sorun olmaz mı? NSLock veya NSRecursiveLock gibi bir şey kullanır mıyım?

3. kat:

Şimdi, yukarıdaki iki durumda, myAwesomeArray bellekte bir iVar oldu. Ya bir veritabanı dosyam varsa, her zaman hafızamda kalmam. Ben bir databaseManagerInstance Ne zaman veritabanı işlemleri yapmak ve bitirdiğim zaman serbest bırakmak istiyorum. Böylece, temel olarak, farklı sınıflar veritabanına erişebilir. Her sınıf kendi örneğini oluşturur DatabaseMangerama temel olarak hepsi aynı tek veritabanı dosyasını kullanıyorlar. Böyle bir durumda yarış koşullarından dolayı verilerin bozulmadığından nasıl emin olabilirim?

Bu benim temellerimin bir kısmını temizlememe yardımcı olacak.


36
2018-06-01 15:51


Menşei


@synchronize Diğer iş parçacıklarının, kilitlediğiniz aynı değişkene erişmesini engeller, belirli bir kod bloğu değil. - Richard J. Ross III
Ah. Anlıyorum. Sanırım @synchronize yönergesi hakkında anladığımdan biraz daha fazlası vardı. Teşekkürler! : D - codeBearer
@codeBearer Şu soruya cevap verdim: stackoverflow.com/a/15393623/412916 Tür. - Jano


Cevaplar:


Kat 1 Genel olarak ne olduğunun anlaşılması @synchronized doğru mu? Ancak, teknik olarak, herhangi bir kod "iplik güvenli" yapmaz. Farklı kilitlerin aynı kilidi aynı anda almasını engeller, ancak kritik bölümleri her zaman aynı senkronizasyon belirtecini kullandığınızdan emin olmanız gerekir. Bunu yapmazsanız, iki parçanın aynı anda kritik bölümler gerçekleştirdiği durumda kendinizi hala bulabilirsiniz. Dokümanları kontrol et.

Kat 2 Çoğu insan NSRecursiveLock'ı kullanmanızı önerir. Senin yerinde olsaydım, GCD kullanırdım. Burada, iplik programlamasından GCD programlamaya nasıl geçiş yapılacağını gösteren harika bir belge var.Bence bu probleme yaklaşım, NSLock'a dayanan birinden çok daha iyi. Özetle, bir seri sıra oluşturun ve görevlerinizi bu sıraya gönderin. Bu şekilde, kritik bölümlerinizin seri olarak işlendiğinden emin olursunuz, bu nedenle herhangi bir zamanda yalnızca bir kritik bölüm gerçekleştirilir.

3. katla Bu aynı Kat 2, sadece daha spesifik. Veri tabanı bir kaynaktır, bir çok şekilde diziyle veya başka bir şeyle aynıdır. Veritabanı programlama bağlamında GCD tabanlı yaklaşımı görmek istiyorsanız, fmdb uygulamasına bir göz atın.. Tam olarak anlattığım şeyi yapıyor Fold2.

Bir yan not olarak 3. katlaBence bu örnekleme DatabaseManager veritabanını kullanmak istediğiniz her zaman ve sonra serbest bırakmak doğru yaklaşımdır. Tek bir veritabanı bağlantısı oluşturmalı ve uygulama oturumunuzda saklamanız gerektiğini düşünüyorum. Bu şekilde yönetmek daha kolaydır. Yine, fmdb bunun nasıl başarılabileceği konusunda harika bir örnektir.

Düzenle GCD'yi kullanmak istemiyorsanız, evet, bir çeşit kilitleme mekanizması kullanmanız gerekecek ve evet, NSRecursiveLock Eğer yöntemlerinizde özyinelemeyi kullanırsanız, çıkma engelleri önler, bu yüzden iyi bir seçimdir ( @synchronized). Bununla birlikte, tek bir yakalama olabilir. Bir çok iş parçacığının aynı kaynağı beklemesi olasıysa ve erişim aldıkları sırayla alakalıysa, o zaman NSRecursiveLock yeterli değil. Bu durumu hâlâ yönetebilirsiniz. NSConditionama bana güvenin, bu durumda GCD kullanarak çok zaman kazanacaksınız. Konuların sırası uygun değilse, kilitlerle güvendesiniz.


42
2018-06-01 16:17



Cevabınız için teşekkürler! Sanırım bu çoğu şüphemle ilgileniyor. Bir onay gibi: Sanırım bir NSRecursiveLock Kat 3 için de, GCD kullanmıyorsam, doğru mu? Bu benim bilgi tabanım için daha fazla, bu yüzden benim kavramlarımın net olduğunu düşünüyorum. Ayrıca, yan notunuz için teşekkürler 3. katla. Bir sınıfa sahip olmanın en iyisi olmanız ve gerekirse bağlantıyı açıp kapamanız konusunda size katılıyorum. Birden fazla örnekleme yapan bazı kodları yeniden yazdım - ne kabus oldu. > _ <Daha iyi bir fotoğraf vermek için soruya koymuştum. :) - codeBearer
Yardımınız için mutluyum, düzenlenmiş cevabımla ilgili bir göz atın. - lawicko
Mükemmel! Tekrar teşekkürler! : D myKnowledge ++ Tüm yeni projelerimizde GCD kullanmaya başlamayı planlıyorum ve sağladığınız linkler harika bir başlangıç ​​noktası! :) - codeBearer
GCD ve Seri Sıra için +1. Sorunlardan kaçınmanın iyi bir yolu - rogerstone
Katlama 2'nin bir varyasyonunda, okuyucu yazar desenini kullanabilirsiniz (eşzamanlı sırayla, oku dispatch_syncile yaz dispatch_barrier_async) tartışıldığı gibi WWDC 2012 videosu - Asenkron Tasarım Desenleri. - Rob


De olduğu gibi Swift 3 içinde WWDC 2016 Oturum Oturumu 720 Swift'de GCD ile Eşzamanlı Programlama 3, kullanmalısın queue

class MyObject {
  private let internalState: Int
  private let internalQueue: DispatchQueue

  var state: Int {
    get {
      return internalQueue.sync { internalState }
    }

    set (newValue) {
      internalQueue.sync { internalState = newValue }
    }
  }
}

5
2018-06-17 23:27





Access (okuma ve yazma) yöntemlerinin kilitlenmesini sağlamak için alt sınıf NSMutableArray. Gibi bir şey:

@interface MySafeMutableArray : NSMutableArray { NSRecursiveLock *lock; } @end

@implementation MySafeMutableArray

- (void)addObject:(id)obj {
  [self.lock lock];
  [super addObject: obj];
  [self.lock unlock];
}

// ...
@end

Bu yaklaşım, dizinin bir parçası olarak kilitlemeyi kapsüller. Kullanıcıların çağrılarını değiştirmelerine gerek yoktur (ancak erişimin kritik öneme sahip olması durumunda erişimi engelleyebileceklerini / bekleyebileceklerini bilmeleri gerekebilir). Bu yaklaşımın önemli bir avantajı, kilitleri kullanmamaya karar verirseniz, MySafeMutableArray'ı gönderim sıralarını kullanmak için yeniden uygulayabileceğinizi veya belirli bir probleminiz için en iyi olanı seçebilmenizdir. Örneğin, addObject öğesini şu şekilde uygulayabilirsiniz:

- (void)addObject:(id)obj {
    dispatch_sync (self.queue, ^{ [super addObject: obj] });
}

Not: Eğer kilit kullanıyorsanız, NSLock değil NSRecursiveLock'a ihtiyacınız olacaktır, çünkü addObject'in Objective-C uygulamalarını bilmiyorsunuzdur, vb.


1
2018-06-01 16:06



Cevabın için teşekkürler. Bir nesnenin güvenli bir alt sınıfını yapmak için bu iyi bir fikir. :) - codeBearer
İplik güvenliğini arayanlara bırakıyorsanız (başka bir deyişle, güvenli bir alt sınıf oluşturmuyorsunuz, nasıl uygulandıysanız), o zaman tekrar edeceksiniz, sorunlarınız için kendinizi ayarlayınız. Spin teknolojisi hakkında biraz bilgi sahibi olursun. İyi şanslar. - GoZoner
Muhteşem. Bu bahşiş için teşekkürler. Ben ona bakacağım. : D - codeBearer
Herhangi bir alt sınıfı NSArray başka birçok yöntemin de uygulanması gerekiyor; Bu iyi bir örnek değil. - ilya n.
@ilyan. İsteğinizle ilgili herhangi bir ayrıntı vermezseniz, yorumunuz iyi değildir. Daha spesifik olabilir misin? - Stephen Paul