Site icon AppTractor

В чём отличие асинхронности и многопоточности — вопросы с собеседований

Асинхронность и многопоточность — это два разных подхода к выполнению задач параллельно или конкурентно, однако они решают схожие проблемы.

Асинхронность — это способ выполнения задач без ожидания завершения предыдущих. Представьте, что вы готовите ужин.

Вы ставите воду на плиту для кипячения (начали задачу). 
Пока вода кипит, вы режете овощи (делаете другую задачу). 
Когда вода закипит, вы добавляете макароны (возвращаетесь к первой задаче).

Вы не стоите и не ждёте, пока вода закипит. Вместо этого, вы выполняете другие задачи, и возвращаетесь к первой, когда она готова.

Многопоточность — это способ выполнения нескольких задач одновременно на разных потоках. Вернёмся к примеру с ужином.

Вы готовите ужин (первая задача). 
В это же время ваш друг помогает вам и моет посуду (вторая задача).

Теперь обе задачи выполняются одновременно и независимо друг от друга. Это позволяет сэкономить время, но требует координации (например, чтобы не столкнуться на кухне).

Асинхронность

Асинхронное программирование позволяет выполнять задачи не блокируя основной поток выполнения. Это достигается за счёт использования конструкций, таких как коллбэки, промисы, async/await. Основные характеристики:

  1. Не блокирующее выполнение: Асинхронные операции не блокируют основной поток выполнения. Это полезно для задач, которые могут ожидать внешние ресурсы, такие как запросы к базе данных или сетевые операции.
  2. Однопоточность: В большинстве реализаций (например, JavaScript) асинхронное программирование выполняется в одном потоке. Операции выполняются в фоновом режиме, но их завершение обрабатывается в основном потоке через очередь задач (event loop).
  3. Event Loop: Асинхронные операции управляются циклом событий, который постоянно проверяет, есть ли завершённые задачи, которые нужно обработать.

Многопоточность

Многопоточность позволяет выполнять несколько потоков (нитей) одновременно. Основные характеристики:

  1. Параллельное выполнение: Потоки могут выполняться параллельно на разных ядрах процессора, что позволяет улучшить производительность для вычислительно сложных задач.
  2. Блокирующие операции: Каждый поток может выполнять блокирующие операции, но другие потоки продолжают работу.
  3. Контекст переключения: Операционная система управляет переключением между потоками, что может приводить к накладным расходам на контекстное переключение.
  4. Сложность синхронизации: Многопоточность требует управления доступом к общим ресурсам и синхронизацией потоков, чтобы избежать гонок и взаимных блокировок.

Основные отличия:

Модель выполнения: Асинхронность обычно работает в одном потоке и использует цикл событий для обработки задач. Многопоточность использует несколько потоков для параллельного выполнения.

Использование ресурсов: Асинхронность экономит ресурсы за счёт избежания блокировки потоков, тогда как многопоточность может улучшить производительность на многоядерных системах за счёт реального параллелизма.

Простота: Асинхронность проще в плане управления состоянием и синхронизацией, поскольку работает в одном потоке. Многопоточность требует управления синхронизацией и избегания гонок данных.

Контекст переключения: Асинхронность не требует контекстного переключения, тогда как многопоточность зависит от частых переключений контекста, что может снижать производительность.

Таким образом, выбор между асинхронностью и многопоточностью зависит от конкретных задач и контекста их выполнения.

Примеры на Swift

Давайте рассмотрим примеры асинхронного программирования и многопоточности на языке Swift.

Асинхронность в Swift

В Swift для асинхронного программирования часто используется GCD (Grand Central Dispatch) или новая синтаксическая конструкция async/await, введённая в Swift 5.5.

Пример с использованием async/await:

import Foundation

// Функция, которая симулирует асинхронный запрос
func fetchData() async throws -> String {
    // Задержка для симуляции сетевого запроса
    try await Task.sleep(nanoseconds: 2 * 1_000_000_000)
    return "Fetched data"
}

// Асинхронная функция для вызова fetchData
func fetchDataAsync() {
    Task {
        do {
            let data = try await fetchData()
            print(data)
        } catch {
            print("Ошибка: \(error)")
        }
    }
}

// Вызов функции
fetchDataAsync()

Пример с использованием GCD:

import Foundation

func fetchData(completion: @escaping (String) -> Void) {
    DispatchQueue.global().async {
        // Задержка для симуляции сетевого запроса
        sleep(2)
        let data = "Fetched data"
        DispatchQueue.main.async {
            completion(data)
        }
    }
}

// Вызов функции
fetchData { data in
    print(data)
}

Многопоточность в Swift

В Swift многопоточность можно реализовать с помощью класса Thread или, более часто, с использованием GCD.

Пример с использованием класса Thread:

import Foundation

func printNumbers() {
    for i in 0..<5 {
        print(i)
    }
}

func printLetters() {
    for letter in ["a", "b", "c", "d", "e"] {
        print(letter)
    }
}

let thread1 = Thread {
    printNumbers()
}

let thread2 = Thread {
    printLetters()
}

thread1.start()
thread2.start()

thread1.join()
thread2.join()

Пример с использованием GCD:

import Foundation

func printNumbers() {
    for i in 0..<5 {
        print(i)
    }
}

func printLetters() {
    for letter in ["a", "b", "c", "d", "e"] {
        print(letter)
    }
}

let queue = DispatchQueue.global()

queue.async {
    printNumbers()
}

queue.async {
    printLetters()
}

Оба подхода имеют свои сильные стороны и применяются в зависимости от конкретных требований задачи.

Exit mobile version