İleti dizilerini yönetme

Binder'ın iş parçacığı modeli, bu çağrılar uzak bir işleme yönelik olsa bile yerel işlev çağrılarını kolaylaştırmak için tasarlanmıştır. Daha net bir şekilde ifade etmek gerekirse, bir düğüme ev sahipliği yapan herhangi bir işlemin, bu işlemde barındırılan düğümlere yönelik işlemleri işlemek için bir veya daha fazla bağlayıcı iş parçacığı havuzuna sahip olması gerekir.

Eşzamanlı ve eşzamansız işlemler

Binder, eşzamanlı ve eşzamansız işlemleri destekler. Aşağıdaki bölümlerde, her işlem türünün nasıl yürütüldüğü açıklanmaktadır.

Eşzamanlı işlemler

Senkron işlemler, düğümde yürütülene ve bu işlem için arayan tarafından yanıt alınana kadar engellenir. Aşağıdaki şekilde, senkron bir işlemin nasıl yürütüldüğü gösterilmektedir:

Eşzamanlı işlem.

1.şekil Eşzamanlı işlem.

Binder, senkron bir işlemi yürütmek için aşağıdakileri yapar:

  1. Hedef iş parçacığı havuzundaki (T2 ve T3) iş parçacıkları, gelen işleri beklemek için çekirdek sürücüsünü çağırır.
  2. Çekirdek yeni bir işlem alır ve işlemi işlemek için hedef süreçte bir iş parçacığını (T2) uyandırır.
  3. Çağrı iş parçacığı (T1) engeller ve yanıt bekler.
  4. Hedef işlem, işlemi yürütür ve yanıt döndürür.
  5. Hedef işlemdeki (T2) iş parçacığı, yeni iş için çekirdek sürücüsünü geri çağırır.

Eşzamansız işlemler

Asenkron işlemler, tamamlanmak üzere engellenmez. İşlem çekirdeğe geçirildiği anda çağıran iş parçacığının engeli kaldırılır. Aşağıdaki şekilde, eşzamansız bir işlemin nasıl yürütüldüğü gösterilmektedir:

Eşzamansız işlem.

Şekil 2. Eşzamansız işlem.

  1. Hedef iş parçacığı havuzundaki (T2 ve T3) iş parçacıkları, gelen işleri beklemek için çekirdek sürücüsünü çağırır.
  2. Çekirdek yeni bir işlem alır ve işlemi işlemek için hedef süreçte bir iş parçacığını (T2) uyandırır.
  3. Arama iş parçacığı (T1) yürütülmeye devam eder.
  4. Hedef işlem, işlemi yürütür ve yanıt döndürür.
  5. Hedef işlemdeki (T2) iş parçacığı, yeni iş için çekirdek sürücüsünü geri çağırır.

Eşzamanlı veya eşzamansız bir işlevi tanımlama

AIDL dosyasında oneway olarak işaretlenen işlevler asenkron olur. Örneğin:

oneway void someCall();

oneway olarak işaretlenmeyen işlevler, void döndürse bile eşzamanlı işlevlerdir.

Eşzamansız işlemlerin serileştirilmesi

Binder, herhangi bir tek düğümdeki eşzamansız işlemleri serileştirir. Aşağıdaki şekilde, bağlayıcının eşzamansız işlemleri nasıl serileştirdiği gösterilmektedir:

Eşzamansız işlemlerin serileştirilmesi.

3.Şekil Eşzamansız işlemlerin serileştirilmesi.

  1. Hedef iş parçacığı havuzundaki iş parçacıkları (B1 ve B2), gelen işi beklemek için çekirdek sürücüsünü çağırır.
  2. Aynı düğümdeki (N1) iki işlem (T1 ve T2) çekirdeğe gönderilir.
  3. Çekirdek yeni işlemler alır ve aynı düğümden (N1) geldikleri için bunları serileştirir.
  4. Farklı bir düğümde (N2) başka bir işlem çekirdeğe gönderilir.
  5. Çekirdek, üçüncü işlemi alır ve işlemi işlemek için hedef işlemde bir iş parçacığını (B2) uyandırır.
  6. Hedef işlemler her işlemi yürütür ve bir yanıt döndürür.

İç içe işlemler

Senkron işlemler iç içe yerleştirilebilir. Bir işlemi işleyen bir iş parçacığı yeni bir işlem yayınlayabilir. İç içe yerleştirilmiş işlem, farklı bir işleme veya mevcut işlemi aldığınız aynı işleme yönelik olabilir. Bu davranış, yerel işlev çağrılarını taklit eder. Örneğin, iç içe yerleştirilmiş işlevlere sahip bir işleviniz olduğunu varsayalım:

def outer_function(x):
    def inner_function(y):
        def inner_inner_function(z):

Bunlar yerel çağrılarsa aynı iş parçacığında yürütülür. Özellikle, inner_function işlevini çağıran aynı zamanda inner_inner_function işlevini uygulayan düğüme ev sahipliği yapan süreçse inner_inner_function işlevine yapılan çağrı aynı iş parçacığında yürütülür.

Aşağıdaki şekilde, bağlayıcının iç içe yerleştirilmiş işlemleri nasıl işlediği gösterilmektedir:

İç içe yerleştirilmiş işlemler.

Şekil 4. İç içe yerleştirilmiş işlemler.

  1. İş parçacığı A1, foo() çalıştırılmasını istiyor.
  2. Bu istek kapsamında, B1 iş parçacığı bar() işlevini çalıştırır. A işlevi ise aynı A1 iş parçacığında çalışır.

Aşağıdaki şekilde, bar() uygulayan düğüm farklı bir süreçteyse iş parçacığı yürütme işlemi gösterilmektedir:

Farklı süreçlerdeki iç içe yerleştirilmiş işlemler.

5.şekil Farklı süreçlerdeki iç içe yerleştirilmiş işlemler.

  1. İş parçacığı A1, foo() çalıştırılmasını istiyor.
  2. Bu isteğin bir parçası olarak, B1 ileti dizisi, başka bir ileti dizisi olan C1'de çalışan bar()'yı çalıştırır.

Aşağıdaki şekilde, iş parçacığının işlem zincirinin herhangi bir yerinde aynı süreci nasıl yeniden kullandığı gösterilmektedir:

Bir ileti dizisini yeniden kullanan iç içe yerleştirilmiş işlemler.

6.şekil Bir ileti dizisini yeniden kullanan iç içe yerleştirilmiş işlemler.

  1. A işlemi, B işlemine çağrı yapar.
  2. B sürecindeki aramalar C sürecine yönlendirilir.
  3. Daha sonra C süreci, A sürecine geri çağrı yapar ve çekirdek, işlem zincirinin bir parçası olan A sürecindeki A1 iş parçacığını yeniden kullanır.

Eşzamansız işlemlerde iç içe yerleştirme rol oynamaz. İstemci, eşzamansız bir işlemin sonucunu beklemediği için iç içe yerleştirme yoktur. Asenkron bir işlemin işleyicisi, bu asenkron işlemi veren süreçte bir çağrı yaparsa bu işlem, söz konusu süreçteki herhangi bir boş iş parçacığında işlenebilir.

Kilitlenmeleri önleme

Aşağıdaki resimde yaygın bir kilitlenme durumu gösterilmektedir:

Yaygın kilitlenme.

Şekil 7. Yaygın kilitlenme.

  1. A işlemi, MA mutex'ini alır ve MB mutex'ini de almaya çalışan B işlemine bir bağlayıcı çağrısı (T1) yapar.
  2. Aynı anda, B işlemi MB mutex'ini alır ve MA mutex'ini almaya çalışan A işlemine bir bağlayıcı çağrısı (T2) yapar.

Bu işlemler çakışırsa her işlem, diğer işlemin mutex'i serbest bırakmasını beklerken kendi sürecinde mutex alabilir ve bu da kilitlenmeye neden olur.

Binder kullanırken kilitlenmeleri önlemek için binder çağrısı yaparken herhangi bir kilidi tutmayın.

Kilit sıralama kuralları ve kilitlenmeler

Tek bir yürütme ortamında kilitlenme genellikle bir kilit sıralama kuralıyla önlenir. Ancak, özellikle kod güncellendikçe süreçler ve kod tabanları arasında çağrı yaparken bir sıralama kuralını korumak ve koordine etmek imkansızdır.

Tek karşılıklı dışlama ve kilitlenmeler

İç içe yerleştirilmiş işlemlerle, B işlemi, A işleminde aynı iş parçacığına doğrudan geri çağrı yapabilir. A işlemi, karşılıklı dışlama kilidi tutar. Bu nedenle, beklenmeyen özyineleme nedeniyle tek bir mutex ile kilitlenme elde etmek hâlâ mümkündür.

Eşzamanlı aramalar ve kilitlenmeler

Asenkron bağlayıcı çağrıları tamamlanmak üzere engellemez ancak asenkron çağrılar için de kilit tutmaktan kaçınmanız gerekir. Kilit tutuyorsanız tek yönlü bir görüşme yanlışlıkla eşzamanlı görüşmeye dönüştürülürse kilitleme sorunları yaşayabilirsiniz.

Tek bağlayıcı iş parçacığı ve kilitlenmeler

Binder'ın işlem modeli yeniden girişe izin verir. Bu nedenle, bir işlemin tek bir bağlayıcı iş parçacığı olsa bile kilitleme yapmanız gerekir. Örneğin, tek iş parçacıklı bir işlemde (A) bir liste üzerinde yineleme yaptığınızı varsayalım. Listedeki her öğe için giden bir bağlayıcı işlemi yaparsınız. Çağırdığınız işlevin uygulanması, A işleminde barındırılan bir düğüme yeni bir bağlayıcı işlemi yapıyorsa bu işlem, listeyi yineleyen aynı iş parçacığında gerçekleştirilir. Bu işlemin uygulanması aynı listeyi değiştirirse daha sonra liste üzerinde yineleme yapmaya devam ettiğinizde sorun yaşayabilirsiniz.

İş parçacığı havuzu boyutunu yapılandırma

Bir hizmetin birden fazla istemcisi olduğunda iş parçacığı havuzuna daha fazla iş parçacığı eklemek, çekişmeyi azaltabilir ve daha fazla çağrıya paralel olarak hizmet verebilir. Eşzamanlılığı doğru şekilde ele aldıktan sonra daha fazla iş parçacığı ekleyebilirsiniz. Bazı iş parçacıklarının sessiz iş yükleri sırasında kullanılmamasına neden olabilecek daha fazla iş parçacığı eklenmesiyle ilgili bir sorun.

İşlemler, yapılandırılmış bir maksimum değere ulaşana kadar isteğe bağlı olarak oluşturulur. Bir bağlayıcı iş parçacığı oluşturulduktan sonra, onu barındıran işlem sona erene kadar etkin kalır.

libbinder kitaplığında varsayılan olarak 15 iş parçacığı bulunur. Bu değeri değiştirmek için setThreadPoolMaxThreadCount kullanın:

using ::android::ProcessState;
ProcessState::self()->setThreadPoolMaxThreadCount(size_t maxThreads);