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


#1

Я более менее разобрался с понятиями очередей, потоков и их видов, но на практике очень сложно сопоставить с реальными задачами. Я понял что отностельно главного потока я могу создать параллельный поток с sync или asyc очередью, но есть одно но. С concurrent потоками я разобрался как работать относительно main thread, а вот как работать с serial относиттельно главного потока или любого другого потока и как о себя ведёт при asyn и sync непонятно. Кто может объясните пожалуйста про serialQueue) Буду очень благодарен. Было бы круто если покажете на рисунке. Для парллельного потока это выглядит как две параллельные линии а для serial как


#2

Относительно любой очереди, любая другая (или та же самая) очередь, или блокирует её (sync) или не блокирует (async).

В последовательной очереди всего один поток, когда вы выполняете код асинхронно, что бы очередь не блокировалась ваш код перемещаться в её конец, то есть асинхронный код выполниться когда очередь освободиться:

DispatchQueue.main.async {
    print("0")
}
print("1")
print("2")
print("3")

// Output: 1, 2, 3, 0 // Всегда в таком порядке

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

let queue = DispatchQueue.global(qos: .default)
queue.sync {
    queue.async {
        print("0")
    }
    print("1")
    print("2")
    print("3")
}

// Output: 1, 0, 2, 3 // Может быть любой другой порядок

#3

А можете тоже самое и про синхронное рассказать(прям те же примеры но с синхронной очередью)? просто мне не понятно зачем использовать параллельную синхронную очередь ведь в ней задачи будут выполнятся также как и параллельной очереди или нет?


#4

Синхронное выполнение блокирует очередь, точнее передаёт управление.

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

let queue = DispatchQueue(label: "background")

print("isMainThread: \(Thread.isMainThread)") // isMainThread: true
queue.async {
    print("isMainThread: \(Thread.isMainThread)") // isMainThread: false
}

А вот если выполнить синхронно, то окажется что это один и тот же поток:

let queue = DispatchQueue(label: "background")

print("isMainThread: \(Thread.isMainThread)") // isMainThread: true
queue.sync {
    print("isMainThread: \(Thread.isMainThread)") // isMainThread: true
}

То есть sync как бы возвращает в родительскую очередь (и поток) из любых других, это позволяет избежать race condition например:

let queue0 = DispatchQueue(label: "background1")
let queue1 = DispatchQueue(label: "background2")
let queue2 = DispatchQueue.global()

queue0.async {
    print("Thread: \(Thread.current)")
    queue1.sync {
        print("Thread: \(Thread.current)")
    }
    queue2.sync {
        print("Thread: \(Thread.current)")
    }
}

В этом плане последовательная и параллельная очереди практически не отличаются, разве что, если в последовательной очереди (в самой себе) попробовать выполнить код синхронно, будет deadlock:

let queue = DispatchQueue(label: "background")
queue.async {
    queue.sync { // <- deadlock
        print("Hello")
    }
}

В параллельной не будет:

let queue = DispatchQueue.global()
queue.async {
    queue.sync {
        print("Hello")
    }
}

#5

let queue = DispatchQueue.global(qos: .default) queue.sync { queue.async { print(“0”) } print(“1”) print(“2”) print(“3”) }
[/quote] а здесь зачем было сделано сначала sync а затем async?


#6

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


#7

Спасибо большое) Очень помогло


#8

Можно ещё вопрос) Например я выношу асинхронно (чтобы главный поток не тормозил) код из main thread как у меня будут выполняться задачи

DispathQueue(label: “new”, attribute: .concurrent).async{

print(“1”)
print(“2”)

}
принты будут выполняться на разных потоках?

или они будут выполнять в разных потоках так

DispathQueue(label: “new”, attribute: .concurrent).async{

 DispatchQueue.(label: "new2", attribute: .concurrent).async{
  print("1")

}
print(“2”)

}
Не могу понять этого. Или они как то в очереди определённые попадают


#9


#10

Принты в одном блоке (в замыкании если точнее) будут выполняться как обычный код, сверху вниз в одном потоке.


#13

А вот например я:

  1. я вынес задачи в concerrent асинхронно (в одном замыкании) задачи будут выпонятся по наступлению какого либо сигнала (например отсчёт таймера) или как?
  2. я выношу синхронно блокируя главную очередь то задачи будут попорядку выполнятся или так же по наступлению какого либо сигнала?
    Я понимаю что это не столь важно но мне очень интересно узнать
    В литературе ничего про такое нет

#14

Вы хотите познать основы программирования через многопоточность :slight_smile: ?

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