Модель многопоточности Binder разработана для облегчения локальных вызовов функций, даже если эти вызовы могут быть направлены на удаленный процесс. В частности, любой процесс, на котором размещен узел, должен иметь пул из одного или более потоков Binder для обработки транзакций с узлами, размещенными в этом процессе.
Синхронные и асинхронные транзакции
Binder поддерживает синхронные и асинхронные транзакции. В следующих разделах объясняется, как выполняется каждый тип транзакций.
Синхронные транзакции
Синхронные транзакции блокируются до тех пор, пока не будут выполнены на узле и вызывающая сторона не получит ответ на эту транзакцию. На следующем рисунке показано, как выполняется синхронная транзакция:

Рисунок 1. Синхронная транзакция.
Для выполнения синхронной транзакции binder выполняет следующие действия:
- Потоки в целевом пуле потоков (T2 и T3) обращаются к драйверу ядра, чтобы ожидать входящих заданий.
- Ядро получает новую транзакцию и активирует поток (T2) в целевом процессе для обработки транзакции.
- Вызывающий поток (T1) блокируется и ожидает ответа.
- Целевой процесс выполняет транзакцию и возвращает ответ.
- Поток в целевом процессе (T2) обращается к драйверу ядра с просьбой дождаться новой работы.
Асинхронные транзакции
Асинхронные транзакции не блокируются до завершения; вызывающий поток разблокируется, как только транзакция будет передана ядру. На следующем рисунке показано, как выполняется асинхронная транзакция:

Рисунок 2. Асинхронная транзакция.
- Потоки в целевом пуле потоков (T2 и T3) обращаются к драйверу ядра, чтобы ожидать входящих заданий.
- Ядро получает новую транзакцию и активирует поток (T2) в целевом процессе для обработки транзакции.
- Вызывающий поток (T1) продолжает выполнение.
- Целевой процесс выполняет транзакцию и возвращает ответ.
- Поток в целевом процессе (T2) обращается к драйверу ядра с просьбой дождаться новой работы.
Определите, является ли функция синхронной или асинхронной.
Функции, помеченные в AIDL как oneway , являются асинхронными. Например:
oneway void someCall();
Если функция не помечена как oneway , она является синхронной функцией, даже если возвращает void .
Сериализация асинхронных транзакций
Binder сериализует асинхронные транзакции с любого отдельного узла. На следующем рисунке показано, как Binder сериализует асинхронные транзакции:

Рисунок 3. Сериализация асинхронных транзакций.
- Потоки в целевом пуле потоков (B1 и B2) обращаются к драйверу ядра, чтобы ожидать входящих заданий.
- Две транзакции (T1 и T2) на одном и том же узле (N1) отправляются в ядро.
- Ядро получает новые транзакции и, поскольку они поступают от одного и того же узла (N1), сериализует их.
- Другая транзакция на другом узле (N2) отправляется в ядро.
- Ядро получает третью транзакцию и активирует поток (B2) в целевом процессе для обработки транзакции.
- Целевые процессы выполняют каждую транзакцию и возвращают ответ.
Вложенные транзакции
Синхронные транзакции могут быть вложенными; поток, обрабатывающий одну транзакцию, может инициировать новую транзакцию. Вложенная транзакция может быть направлена в другой процесс или в тот же процесс, от которого вы получили текущую транзакцию. Такое поведение имитирует локальные вызовы функций. Например, предположим, у вас есть функция с вложенными функциями:
def outer_function(x):
def inner_function(y):
def inner_inner_function(z):
Если это локальные вызовы, они выполняются в том же потоке. В частности, если вызывающий inner_function процесс также является процессом, на котором размещен узел, реализующий inner_inner_function , то вызов inner_inner_function выполняется в том же потоке.
На следующем рисунке показано, как Binder обрабатывает вложенные транзакции:

Рисунок 4. Вложенные транзакции.
- Поток A1 запрашивает выполнение функции
foo(). - В рамках этого запроса поток B1 запускает функцию
bar(), которую поток A запускает в том же потоке A1.
На следующем рисунке показано выполнение потоков, если узел, реализующий функцию bar() находится в другом процессе:

Рисунок 5. Вложенные транзакции в различных процессах.
- Поток A1 запрашивает выполнение функции
foo(). - В рамках этого запроса поток B1 запускает
bar(), которая выполняется в другом потоке C1.
На следующем рисунке показано, как поток повторно использует один и тот же процесс в любой точке цепочки транзакций:

Рисунок 6. Вложенные транзакции с повторным использованием потока.
- Процесс А вызывает процесс В.
- Процесс B вызывает процесс C.
- Затем процесс C вызывает функцию обратного вызова в процесс A, и ядро повторно использует поток A1 в процессе A, который является частью цепочки транзакций.
Для асинхронных транзакций вложенность не играет роли; клиент не ждет результата асинхронной транзакции, поэтому вложенности нет. Если обработчик асинхронной транзакции вызывает процесс, инициировавший эту асинхронную транзакцию, то эта транзакция может быть обработана в любом свободном потоке этого процесса.
Избегайте тупиковых ситуаций
На следующем изображении показана типичная ситуация тупика:

Рисунок 7. Типичная тупиковая ситуация.
- Процесс A захватывает мьютекс MA и вызывает метод связывания (T1) для процесса B, который также пытается захватить мьютекс MB.
- Одновременно процесс B захватывает мьютекс MB и вызывает метод связывания (T2) у процесса A, который пытается захватить мьютекс MA.
Если эти транзакции перекрываются, каждая транзакция потенциально может занять мьютекс в своем процессе, ожидая, пока другой процесс освободит мьютекс, что приведет к взаимоблокировке.
Во избежание взаимоблокировок при использовании функции binder, не удерживайте блокировку при вызове функции binder.
Правила порядка блокировки и взаимоблокировки
В рамках одной среды выполнения взаимоблокировку часто удается избежать с помощью правила упорядочивания блокировок. Однако при вызовах между процессами и между кодовыми базами, особенно при обновлении кода, поддерживать и координировать правило упорядочивания невозможно.
Одиночный мьютекс и взаимоблокировки
При вложенных транзакциях процесс B может напрямую вызывать функцию обратно в тот же поток процесса A, удерживающий мьютекс. Следовательно, из-за неожиданной рекурсии по-прежнему возможно возникновение взаимоблокировки при наличии одного мьютекса.
Асинхронные вызовы и взаимоблокировки
Хотя асинхронные вызовы связывания не блокируют выполнение до завершения, следует избегать удержания блокировки для асинхронных вызовов. Если вы удерживаете блокировку, могут возникнуть проблемы с блокировкой, если односторонний вызов случайно будет преобразован в синхронный.
Одинарная нить для скрепления и защелки
Транзакционная модель Binder допускает реентерабельность, поэтому даже если процесс имеет один поток Binder, блокировка все равно необходима. Например, предположим, вы перебираете список в однопоточном процессе A. Для каждого элемента списка вы выполняете исходящую транзакцию Binder. Если реализация вызываемой вами функции выполняет новую транзакцию Binder к узлу, размещенному в процессе A, эта транзакция обрабатывается в том же потоке, который перебирал список. Если реализация этой транзакции изменяет тот же список, у вас могут возникнуть проблемы при дальнейшем продолжении перебора списка.
Настройка размера пула потоков
Когда у сервиса несколько клиентов, добавление большего количества потоков в пул потоков может уменьшить конкуренцию и обрабатывать больше вызовов параллельно. После правильной настройки параллелизма можно добавлять больше потоков. Однако добавление большего количества потоков может привести к тому, что некоторые потоки останутся неиспользованными во время периодов низкой нагрузки.
Потоки создаются по требованию до достижения заданного максимального значения. После создания потока-связывателя он остается активным до завершения процесса, в котором он запущен.
В библиотеке libbinder по умолчанию установлено 15 потоков. Используйте setThreadPoolMaxThreadCount , чтобы изменить это значение:
using ::android::ProcessState;
ProcessState::self()->setThreadPoolMaxThreadCount(size_t maxThreads);