Как заставить очередь НЕ запускаться в основном потоке в модульных тестах Swift - PullRequest
1 голос
/ 17 июня 2020

Существует множество примеров того, как заставить код работать в основном потоке. У меня противоположная потребность в целях модульного тестирования. Я хочу, чтобы основной поток не выполнял некоторую работу.

Зачем мне это: я хотел бы проверить, что определенная функция работает правильно, особенно когда она одновременно вызывается из 2 или более фоновых потоков (пока основной поток свободен и доступен) . Это связано с тем, что сама функция использует основной поток, в то время как это типичный вариант использования, когда она будет вызываться из фоновых потоков, возможно, одновременно.

У меня уже есть один тестовый пример, в котором один из вызывающих потоков является основным. Но я также хочу проверить случай, когда основной поток не занят, когда 2 или более других потоков вызывают функцию.

Проблема в том, что, похоже, нет способа оставить основной поток свободным. Например, даже если очередь определена с помощью qoc: .background, основной поток все равно был занят:

private let queue = DispatchQueue(label: "bgqueue", qos: .background, attributes: .concurrent)
let iterations = 10
DispatchQueue.concurrentPerform(iterations: iterations) { _ in

    queue.async { 
        callMyFunction() // still may run on main thread, even with 1-2 iterations
    }
}

Единственный подход, который я могу придумать, - это блокировать все потоки в одном месте (используя CountDownLatch например), а затем перейти к функциям из всех потоков, кроме основных:

let latch = CountDownLatch(count: iterations)

DispatchQueue.concurrentPerform(iterations: iterations) { _ in

    latch.countdown()
    latch.await()
    if !Thread.isMainThread {
        callMyFunction()
    }
}

Проблемы здесь 1 - необходимо убедиться iterations < available threads; 2 - кажется неправильным блокировать основной поток.

Так есть ли лучшее решение? Как исключить основной поток из захвата DispatchQueue работы в рамках модульных тестов?

1 Ответ

0 голосов
/ 17 июня 2020

Пока вы не отправляете в основную очередь (или какую-либо другую очередь, которая использует основную очередь в качестве своей «цели»), async никогда не будет использовать основной поток. Есть несколько интересных оптимизаций с sync, которые влияют на то, какой поток используется (что здесь не имеет значения), но не для async. Асинхронно отправляемая задача будет выполняться в рабочем потоке, связанном с этим QoS, а не в основном потоке.

Итак, go вперед и вставьте утверждение потока в свой тест, чтобы убедиться, что оно не в основном потоке, и я думаю, вы обнаружите, что это нормально:

XCTAssertFalse(Thread.isMainThread, "Shouldn't be on main thread")

Сказанное выше означает, что вы ссылаетесь на тот факт, что «сама функция использует основной поток». Если это так, вы можете легко заблокироваться, если ваш тест блокирует основной поток, ожидая, пока вызываемые функции завершат обработку sh в основном потоке.

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

func testWillNotDeadlock() throws {
    let expectation = self.expectation(description: "testWillNotDeadlock")
    someAsyncMethod {
        expectation.fulfill()
    }
    waitForExpectations(timeout: 100)
}

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

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


Конечно, есть и другие источники потенциальных проблем. Например, вы вызываете async со 100 итерациями. Вам нужно быть осторожным с этим шаблоном, потому что вы можете легко исчерпать все рабочие потоки с помощью этого «взрыва потока». Попытка ввести concurrentPerform (который часто используется для решения проблем разрыва потока) не сработает, если код в закрытии вызывает async.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...