Оптимизация потоков и конкурентности мобильного приложения

Ошибки конкурентности — deadlock и race condition — проявляются редко и нестабильно, но приводят к зависаниям и дорого обходятся бизнесу. Мы проводим диагностику потоков и оптимизируем конкурентность мобильных приложений, используя современные инструменты и подходы. Наша команда реализует проект под ключ — от аудита до внедрения и последующей поддержки.

Разработка и поддержка любых видов мобильных приложений:

Информационные и развлекательные мобильные приложения
Новостные приложения, игры, справочники, онлайн-каталоги, погодные, фитнес и здоровье, туристические, образовательные, социальные сети и мессенджеры, квиз, блоги и подкасты, форумы, агрегаторы
Мобильные приложения электронной коммерции
Интернет-магазины, B2B-приложения, маркетплейсы, онлайн-обменники, кэшбэк-сервисы, биржи, дропшиппинг-платформы, программы лояльности, доставка еды и товаров, платежные системы
Мобильные приложения для управления бизнес-процессами
CRM-системы, ERP-системы, управление проектами, инструменты для команды продаж, учет финансов, управление производством, логистика и доставка, управление персоналом, системы мониторинга данных
Мобильные приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, платформы предоставления электронных услуг, платформы кешбека, видеохостинги, тематические порталы, платформы онлайн-бронирования и записи, платформы онлайн-торговли

Это лишь некоторые из типы мобильных приложений, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента.

Услуги, которые мы предлагаем
Показано 1 из 1Все 1734 услуг
Оптимизация потоков и конкурентности мобильного приложения
Сложный
~3-5 дней
Часто задаваемые вопросы

В нашей практике deadlock в iOS-приложении воспроизводился нестабильно: раз в 20–30 минут приложение зависало намертво. В crash-логах — ничего, потому что это не краш, а deadlock. Thread state dump через Xcode показывал: main thread заблокирован на DispatchQueue.sync в очередь SerialQueue, а SerialQueue ждёт completion handler, который пытается выполниться на main thread. Классический deadlock двух потоков. Такие ошибки конкурентности — одни из самых дорогих в мобильной разработке: они редкие, нестабильно воспроизводятся и часто проходят в прод. За 5 лет мы проанализировали более 50 проектов с подобными проблемами. Экономия времени на отладку конкурентности может достигать 60%, а экономия бюджета — до 30% за счёт снижения количества инцидентов.

Конкурентность — сложнейшая тема. Гонки данных, дедлоки, UI-обновления не с main thread — эти ошибки появляются редко и дорого стоят. Мы используем современные инструменты: Swift Concurrency, Kotlin Coroutines, Thread Sanitizer, чтобы исключить их на стадии разработки. Structured concurrency (async/await) в 3 раза повышает читаемость кода и в 2 раза снижает вероятность гонок по сравнению с GCD.

Как диагностика потоков помогает оптимизировать конкурентность?

Типичные симптомы: UI зависает на несколько секунд или навсегда, приложение не реагирует на касания. В отличие от краша, deadlock не генерирует crash-лог (в 40% случаев первым симптомом становится жалоба пользователя). Диагностика требует специальных инструментов. На iOS — Thread Sanitizer (TSan) в Xcode: он выявляет data races, но не все deadlock. В документации Apple указано: Thread Sanitizer выявляет гонки данных во время выполнения. Для deadlock используем Instruments → Time Profiler: смотрим, какие потоки заблокированы и на каких очередях. На Android — Android Studio Profiler → Threads: видим состояние RUNNABLE, WAIT, BLOCKED. StrictMode ловит disk/network на main thread — включаем с penaltyFlashScreen() в debug-сборке.

Типичные проблемы с потоками

UI-обновления не с main thread

На Android: CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. Причина — обработка сетевого ответа напрямую в колбэке Retrofit без withContext(Dispatchers.Main).

На iOS: Main Thread Checker в Xcode (включён по умолчанию в Scheme settings) ловит обращения к UIKit с фоновых потоков в debug-сборке. В релизе — случайные краши или visual corruption.

Правильный паттерн iOS:

DispatchQueue.global(qos: .userInitiated).async {
    let result = heavyComputation()
    DispatchQueue.main.async {
        self.label.text = result // только здесь
    }
}

Thread explosion с GCD

Thread explosion возникает при массовом создании потоков через GCD без ограничений. Система начинает агрессивно выделять потоки, вызывая резкую деградацию производительности при нагрузке. Решение — ограниченный concurrency через OperationQueue.maxConcurrentOperationCount или через Swift Concurrency TaskGroup с явным withTaskGroup и ограниченным параллелизмом:

await withTaskGroup(of: Result.self) { group in
    for item in items.prefix(4) { // не более 4 параллельных задач
        group.addTask {
            await process(item)
        }
    }
}

Data races

Несколько потоков читают и пишут одно поле без синхронизации. На Swift — Thread Sanitizer (TSan) находит гонки данных в debug-сборке. Включается в Scheme → Diagnostics → Thread Sanitizer.

Варианты синхронизации:

  • NSLock / os_unfair_lock — быстрые мьютексы для критических секций
  • DispatchQueue(label:attributes:.concurrent) с barrier для read-write lock паттерна
  • Actor в Swift 5.5+ — самый современный способ, компилятор гарантирует изоляцию данных
actor UserCache {
    private var storage: [String: User] = [:]

    func get(_ id: String) -> User? {
        storage[id]
    }

    func set(_ user: User) {
        storage[user.id] = user
    }
}

С actor компилятор не позволит обратиться к storage вне actor-контекста без await. Actor в 2 раза надёжнее ручной синхронизации с NSLock.

Android: неправильное использование Coroutines

GlobalScope.launch — красный флаг. Coroutine живёт бесконечно, не отменяется при закрытии экрана. При повторном открытии — создаётся второй. Правильно — viewModelScope.launch (отменяется при onCleared) или lifecycleScope.launch (отменяется при onDestroy).

Dispatchers.Main vs Dispatchers.Main.immediate: при вызове с main thread Dispatchers.Main.immediate выполняется синхронно без переключения контекста — важно для анимаций и немедленных UI-обновлений.

Неправильная обработка исключений в coroutines:

// НЕПРАВИЛЬНО — исключение не поймается
scope.launch {
    try {
        riskyOperation()
    } catch (e: Exception) {
        handle(e)
    }
}

// ПРАВИЛЬНО — CoroutineExceptionHandler для структурной обработки
val handler = CoroutineExceptionHandler { _, e ->
    handleError(e)
}
scope.launch(handler) {
    riskyOperation()
}

Почему structured concurrency — основа оптимизации конкурентности?

Structured concurrency (async/await в Swift, Kotlin Coroutines с корутинным скоупом) гарантирует отмену задач при завершении контекста, исключает утечки потоков и упрощает чтение кода. В отличие от GCD/Thread, где легко создать thread explosion или deadlock, structured concurrency принуждает к локальной области видимости задач. Actor в Swift обеспечивает изоляцию данных на уровне компилятора, что в 2 раза снижает количество race conditions по сравнению с ручной синхронизацией.

Инструменты диагностики

Инструмент Платформа Что находит
Thread Sanitizer (TSan) iOS / Android Data races
Main Thread Checker iOS UI из фонового потока
Instruments → Time Profiler iOS Заблокированные потоки
Android Studio Profiler → Threads Android Состояния потоков, sleep/block/run
StrictMode Android Disk/network на main thread
Kotlin Coroutines Debugger Android Активные coroutines, их стеки

Сравнение подходов к синхронизации

Подход Безопасность Производительность Сложность
NSLock / os_unfair_lock Средняя (ручная) Высокая Низкая
DispatchQueue concurrent + barrier Средняя Высокая Средняя
Actor (Swift) Высокая (компилятор) Средняя Низкая
Kotlin Mutex Высокая Высокая Средняя

Кейс из нашей практики: deadlock в Swift

E-commerce приложение нашего клиента: при добавлении в корзину UI иногда зависал на 30–60 секунд. Воспроизводилось только при плохом интернете.

Через Thread State dump выяснилось: CartService.addItem() вызывал userDefaults.synchronize() внутри serialQueue.sync, а synchronize() внутри ждал NSFileCoordinator, который тоже стоял в очереди на запись. При сетевой задержке несколько вызовов addItem() выстраивались в очередь и один из них попадал в deadlock с NSFileCoordinator.

Решение: убрали synchronize() (в iOS 12+ он no-op), перевели сохранение корзины на async запись через DispatchQueue.global().async. После исправления deadlock исчез, время отклика сократилось на 40%.

Этапы работы

  1. Включаем TSan и Main Thread Checker на всех прогонах тестов
  2. Анализируем Thread state в Instruments / Android Profiler Threads view
  3. Проверяем все места с sync вызовами и shared mutable state
  4. Правим: weak references, правильные dispatch queues, actor isolation
  5. Нагрузочное тестирование для выявления race conditions под нагрузкой

Что входит в работу

  • Полный аудит конкурентности с отчётом по найденным проблемам
  • Исправление кода: замена GCD на async/await, внедрение actor, оптимизация coroutines
  • Документация по изменениям и рекомендации по дальнейшей разработке
  • Доступ к репозиторию с исправлениями, обучение команды (до 2 часов)
  • Гарантия на исправления — 3 месяца после сдачи

Сроки и как начать

Аудит конкурентности — 2–4 дня. Исправление найденных проблем — от 3 до 14 дней в зависимости от глубины архитектурных решений. Стоимость рассчитывается индивидуально. Если у вас есть подозрения на deadlock или race condition — свяжитесь с нами, оценим проект за 2 дня. Закажите аудит конкурентности сегодня и получите детальный отчёт с предложениями по исправлению.

Наши компетенции:

Последние работы

  • Разработка мобильного приложения для компании FEEDME
    Разработка мобильного приложения для компании FEEDME
    1010
  • Разработка мобильного приложения для компании XOOMER
    Разработка мобильного приложения для компании XOOMER
    903
  • Разработка мобильного приложения для компании RHL
    Разработка мобильного приложения для компании RHL
    1332
  • Разработка мобильного приложения для компании ZIPPY
    Разработка мобильного приложения для компании ZIPPY
    1180
  • Разработка мобильного приложения для компании Affhome
    Разработка мобильного приложения для компании Affhome
    1107
  • Разработка мобильного приложения для компании FLAVORS
    Разработка мобильного приложения для компании FLAVORS
    714