Паралельні та послідовні черги в GCD


117

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

  1. Я читаю, що послідовні черги створюються та використовуються для виконання завдань одна за одною. Однак, що станеться, якщо:

    • Я створюю послідовну чергу
    • Я використовую dispatch_async(на щойно створеній нами послідовній черзі) три рази, щоб відправити три блоки A, B, C

    Чи будуть виконані три блоки:

    • в порядку A, B, C, тому що черга є послідовною

      АБО

    • одночасно (в той же час на паралельних нитках), тому що я використовував ASYNC диспетчеризацію
  2. Я читаю, що можу використовувати dispatch_syncв паралельних чергах, щоб виконувати блоки один за одним. У такому випадку ЧОМУ навіть існують послідовні черги, оскільки я завжди можу використовувати одночасну чергу, куди я можу відправити СІНХРОННОЛЬНО стільки блоків, скільки мені хочеться?

    Дякуємо за будь-яке добре пояснення!


Відповіді:


216

Простий приклад: у вас є блок, на виконання якого потрібна хвилина. Ви додаєте її до черги з головної нитки. Давайте розглянемо чотири випадки.

  • async - паралельно: код працює на фоновому потоці. Керування негайно повертається до основного потоку (та інтерфейсу користувача). Блок не може припустити, що це єдиний блок, що працює на цій черзі
  • async - serial: код працює на фоновому потоці. Керування негайно повертається до основної нитки. Блок може припустити, що це єдиний блок, що працює на цій черзі
  • синхронізація - паралельна: код працює на фоновому потоці, але головний потік очікує його завершення, блокуючи будь-які оновлення інтерфейсу користувача. Блок не може припустити, що це єдиний блок, що працює на цій черзі (я міг би додати ще один блок, використовуючи async за кілька секунд раніше)
  • sync - serial: код працює на фоновому потоці, але головний потік чекає його завершення, блокуючи будь-які оновлення інтерфейсу користувача. Блок може припустити, що це єдиний блок, що працює на цій черзі

Очевидно, ви б не використовували жоден з останніх двох для тривалих процесів. Ви зазвичай бачите це при спробі оновити інтерфейс користувача (завжди в основному потоці) з чогось, що може працювати в іншому потоці.


14
Отже, ви говорите мені, що: (1) тип черги (конц або послідовний) є ТОЛЬКО елементом, який вирішує, чи виконуються завдання в порядку чи паралельно ;; (2) тип диспетчеризації (синхронізація чи асинхронізація) говорить лише про те, чи йде виконання АБО, не переходить до наступної інструкції? Я маю на увазі, якщо я відправлю завдання SYNC, код буде блокуватись, поки завдання не закінчуються, незалежно від того, на якій черзі він виконується?
Богдан Олександру

13
@BogdanAlexandru Правильно. Черга диктує політику виконання, а не те, як ви ставите в чергу блок. Синхронізація чекає завершення блоку, асинхронізація не робить.
Яно

2
@swiftBUTCHER До певного моменту, так. Під час створення черги ви можете вказати максимальну кількість потоків. Якщо ви додасте менше завдань, ніж те, вони будуть виконуватися паралельно. Більше того, деякі завдання залишатимуться в черзі, поки не буде доступної потужності.
Стівен Дарлінгтон

2
@PabloA., Основний потік - це послідовна черга, тому є лише справді два випадки. Крім того, це точно так само. Async повертається негайно (і блок, ймовірно, виконується в кінці поточного циклу запуску). Основна проблема є, якщо ви синхронізуєте з основного потоку до основного потоку, і в цьому випадку ви отримуєте тупик.
Стівен Дарлінгтон

1
@ShauketSheikh Ні. Головний потік - це послідовна черга, але не всі послідовні черги є головними потоками. У четвертому пункті головна нитка блокується, чекаючи, коли інша нитка конкурує з її роботою. Якщо послідовна черга була основною темою, ви отримаєте тупик.
Стівен Дарлінгтон

122

Ось пара експериментів, які я зробив, щоб змусити мене зрозуміти ці питання serial, з якими concurrentвиходили черги Grand Central Dispatch.

 func doLongAsyncTaskInSerialQueue() {

   let serialQueue = DispatchQueue(label: "com.queue.Serial")
      for i in 1...5 {
        serialQueue.async {

            if Thread.isMainThread{
                print("task running in main thread")
            }else{
                print("task running in background thread")
            }
            let imgURL = URL(string: "https://upload.wikimedia.org/wikipedia/commons/0/07/Huge_ball_at_Vilnius_center.jpg")!
            let _ = try! Data(contentsOf: imgURL)
            print("\(i) completed downloading")
        }
    }
}

Завдання виконується в іншому потоці (крім основного потоку), коли ви використовуєте async в GCD. Async означає, що виконайте наступний рядок, не чекайте, поки блок виконає, що призводить до не блокування основного потоку та основної черги. З часу своєї послідовної черги всі виконуються в тому порядку, в який вони додаються до послідовної черги. Завдання, що виконуються послідовно, завжди виконуються один за одним одним потоком, пов'язаним з чергою.

func doLongSyncTaskInSerialQueue() {
    let serialQueue = DispatchQueue(label: "com.queue.Serial")
    for i in 1...5 {
        serialQueue.sync {
            if Thread.isMainThread{
                print("task running in main thread")
            }else{
                print("task running in background thread")
            }
            let imgURL = URL(string: "https://upload.wikimedia.org/wikipedia/commons/0/07/Huge_ball_at_Vilnius_center.jpg")!
            let _ = try! Data(contentsOf: imgURL)
            print("\(i) completed downloading")
        }
    }
}

Завдання може запускатися в основному потоці при використанні синхронізації в GCD. Синхронізація запускає блок по даній черзі і чекає його завершення, що призводить до блокування основного потоку або основної черги. Оскільки головній черзі потрібно зачекати, поки відправлений блок завершиться, головний потік буде доступний для обробки блоків з черг, крім основна черга. Тому існує ймовірність виконання коду у фоновій черзі, фактично може виконуватись на головному потоці. Оскільки її послідовна черга, всі виконуються в порядку їх додавання (FIFO).

func doLongASyncTaskInConcurrentQueue() {
    let concurrentQueue = DispatchQueue(label: "com.queue.Concurrent", attributes: .concurrent)
    for i in 1...5 {
        concurrentQueue.async {
            if Thread.isMainThread{
                print("task running in main thread")
            }else{
                print("task running in background thread")
            }
            let imgURL = URL(string: "https://upload.wikimedia.org/wikipedia/commons/0/07/Huge_ball_at_Vilnius_center.jpg")!
            let _ = try! Data(contentsOf: imgURL)
            print("\(i) completed downloading")
        }
        print("\(i) executing")
    }
}

Завдання виконується у фоновому потоці, коли ви використовуєте async у GCD. Async означає, що виконайте наступний рядок, не чекайте, поки блок виконає, що призводить до не блокування основного потоку. Пам'ятайте, що в паралельній черзі завдання обробляються в тому порядку, коли вони додаються до черги, але з різними потоками, приєднаними до черги. Пам'ятайте, що вони не повинні закінчувати завдання, оскільки порядок їх додавання до черги. Порядок завдання відрізняється щоразу, коли нитки створюються як обов'язково автоматично. Завдання виконується паралельно. Якщо досягнуто більше (maxConcurrentOperationCount), деякі завдання будуть вести себе як послідовне, поки нитка не буде вільною.

func doLongSyncTaskInConcurrentQueue() {
  let concurrentQueue = DispatchQueue(label: "com.queue.Concurrent", attributes: .concurrent)
    for i in 1...5 {
        concurrentQueue.sync {
            if Thread.isMainThread{
                print("task running in main thread")
            }else{
                print("task running in background thread")
            }
            let imgURL = URL(string: "https://upload.wikimedia.org/wikipedia/commons/0/07/Huge_ball_at_Vilnius_center.jpg")!
            let _ = try! Data(contentsOf: imgURL)
            print("\(i) completed downloading")
        }
        print("\(i) executed")
    }
}

Завдання може запускатися в основному потоці при використанні синхронізації в GCD. Синхронізація запускає блок по даній черзі і чекає його завершення, що призводить до блокування основного потоку або основної черги. Оскільки головній черзі потрібно зачекати, поки відправлений блок завершиться, головний потік буде доступний для обробки блоків з черг, крім основна черга. Тому існує ймовірність виконання коду у фоновій черзі, фактично, може бути виконана в основному потоці. Оскільки його паралельна чергу, завдання можуть не закінчуватися в тому порядку, коли вони додані до черги. Але при синхронній роботі це відбувається, хоча вони можуть оброблятися різними потоками. Отже, він поводиться так, як це серійна черга.

Ось короткий опис цих експериментів

Пам'ятайте, що за допомогою GCD ви лише додаєте завдання до черги та виконуєте завдання з цієї черги. Черга відправляє ваше завдання або в основний, або в фоновий потік, залежно від того, синхронна чи асинхронна операція. Типи черг - це послідовна, паралельна, основна черговість відправки. Всі завдання, які ви виконуєте, виконуються за замовчуванням з головної черги відправки. Вже є чотири заздалегідь визначені глобальні паралельні черги для вашої програми для використання та одна основна черга (DispatchQueue.main). Ви Ви також можете вручну створити власну чергу і виконати завдання з цієї черги.

Завдання, пов’язане з користувальницьким інтерфейсом, завжди повинно виконуватися з основного потоку шляхом передачі завдання до основної черги. Утиліта короткої руки полягає в тому, що DispatchQueue.main.sync/asyncпов’язані з мережею / важкі операції завжди повинні виконуватись асинхронно, незалежно від того, що коли-небудь потоком ви використовуєте або основну, або основну

EDIT: Однак, є випадки, коли вам потрібно синхронно виконувати операції з мережевими дзвінками у фоновому потоці без заморожування інтерфейсу користувача (переохолодження OAuth Token і зачекайте, чи вдасться це чи ні). Вам потрібно загорнути цей метод всередину асинхронної операції. Це ваш важкий Операції виконуються в порядку та без блокування основної нитки.

func doMultipleSyncTaskWithinAsynchronousOperation() {
    let concurrentQueue = DispatchQueue(label: "com.queue.Concurrent", attributes: .concurrent)
    concurrentQueue.async {
        let concurrentQueue = DispatchQueue.global(qos: DispatchQoS.QoSClass.default)
        for i in 1...5 {
            concurrentQueue.sync {
                let imgURL = URL(string: "https://upload.wikimedia.org/wikipedia/commons/0/07/Huge_ball_at_Vilnius_center.jpg")!
                let _ = try! Data(contentsOf: imgURL)
                print("\(i) completed downloading")
            }
            print("\(i) executed")
        }
    }
}

EDIT EDIT: Демо-відео можна переглянути тут


Чудова демонстрація .... наступний рядок не чекайте, поки блок виконає, що призводить до не блокування головної нитки, ось чому, якщо ви використовуєте точки прориву на фоновій нитці, він перейде до того, }що він справді не виконується на той момент
Honey

@ Цього ледачого iOS Хлопця, я все ще не розумію різниці між паралельною програмою async та async serial. Що означає використання будь-якого. Вони обидва працюють у фоновому режимі, не порушуючи інтерфейс користувача. І навіщо вам коли-небудь використовувати синхронізацію? Чи не все синхронізація коду. один за одним?
еоніст

1
@GitSyncApp ви можете подивитися відео тут
Anish Parajuli 웃

@ Той ледачий хлопець з iOS 웃: thx для створення цього. Я розмістив на слабій швидкості. Було б 👌 Якби ви могли також написати про DispatchGroup та DispatchWorkItem. : D
еоніст

Я перевірив свій останній, concurrentQueue.syncз doLongSyncTaskInConcurrentQueue()функції, він виводить основну нитку, Task will run in different threadздається , не так.
габлер

54

По-перше, важливо знати різницю між потоками та чергами та тим, що насправді робить GCD. Коли ми використовуємо диспетчерські черги (через GCD), ми дійсно стаємо в чергу, а не внизу. Рамка Dispatch була розроблена спеціально для того, щоб позбавити нас від нарізування різьби, оскільки Apple визнає, що "реалізувати правильне рішення про різьблення може стати надзвичайно важким, якщо не [інколи] неможливим досягти". Тому, щоб одночасно виконувати завдання (завдання, які ми не хочемо заморожувати користувальницький інтерфейс), все, що нам потрібно зробити, - це створити чергу цих завдань і передати її GCD. І GCD обробляє всі пов'язані з ними нарізки. Тому все, що ми насправді робимо, - це чергування.

Друге, що потрібно знати відразу - це завдання. Завдання - це весь код у цьому блоці черги (не в черзі, тому що ми можемо додавати речі до черги весь час, але не в межах закриття, коли ми додали його до черги). Завдання іноді називають блоком, а блок іноді називають завданням (але вони більш відомі як завдання, особливо у спільноті Swift). І незалежно від того, скільки або мало коду, весь код у фігурних дужках вважається єдиним завданням:

serialQueue.async {
    // this is one task
    // it can be any number of lines with any number of methods
}
serialQueue.async {
    // this is another task added to the same queue
    // this queue now has two tasks
}

І очевидно згадати, що одночасне просто означає одночасно з іншими речами, а серійні засоби одна за одною (ніколи одночасно). Серіалізувати щось або поставити щось серійно, просто означає виконати це від початку до кінця в його порядку зліва направо, зверху вниз, безперебійно.

Існує два типи черг - послідовна та паралельна, але всі черги паралельно одна одній . Той факт, що ви хочете запустити будь-який код "у фоновому режимі", означає, що ви хочете виконувати його одночасно з іншим потоком (зазвичай основним потоком). Тому всі черги відправлення, послідовні чи паралельні, виконують свої завдання одночасно відносно інших черг . Будь-яка серіалізація, що виконується чергами (послідовними чергами), стосується лише завдань у межах однієї [послідовної] черги відправки (як у прикладі вище, коли в одній послідовній черзі є два завдання; ці завдання будуть виконані одна після інше, ніколи одночасно).

СЕРІЙНІ ЧАСИ (часто відомі як приватні черги диспетчеризації) гарантують виконання завдань по черзі від початку до кінця в тому порядку, який вони додавали до цієї конкретної черги. Це єдина гарантія серіалізації де-небудь в обговоренні черг відправки- що конкретні завдання в межах певної послідовної черги виконуються послідовно. Однак послідовні черги можуть запускатися одночасно з іншими послідовними чергами, якщо вони є окремими чергами, оскільки, знову ж таки, всі черги паралельно одна одній. Усі завдання виконуються на різних темах, але не кожне завдання гарантується для виконання на одній і тій же темі (не важливо, але цікаво знати). І рамки iOS не мають готових до використання послідовних черг, ви повинні їх створити. Приватні (не глобальні) черги за замовчуванням є послідовними, тому для створення послідовної черги:

let serialQueue = DispatchQueue(label: "serial")

Ви можете зробити його одночасно через властивість атрибута:

let concurrentQueue = DispatchQueue(label: "concurrent", attributes: [.concurrent])

Але на даний момент, якщо ви не додаєте жодних інших атрибутів до приватної черги, Apple рекомендує просто використовувати одну з їхніх готових до роботи глобальних черг (які є одночасно). У нижній частині цієї відповіді ви побачите інший спосіб створення послідовних черг (використовуючи цільову властивість), саме так Apple рекомендує це робити (для більш ефективного управління ресурсами). Але наразі маркування цього достатньо.

СУЧАСНІ ЧАСИ (часто відомі як глобальні черги відправлення) можуть виконувати завдання одночасно; завдання, однак, гарантовано починаються в тому порядку, коли вони були додані до цієї конкретної черги, але на відміну від послідовних черг, черга не чекає, коли перше завдання закінчиться перед початком другого завдання. Завдання (як і послідовні черги) виконуються на різних потоках і (як і в послідовних чергах), не кожне завдання гарантується для виконання на одному потоці (не важливо, але цікаво знати). Рамка iOS оснащена чотирма готовими до використання одночасно чергами. Ви можете створити паралельну чергу за допомогою наведеного вище прикладу або за допомогою однієї з глобальних черг Apple (що зазвичай рекомендується):

let concurrentQueue = DispatchQueue.global(qos: .default)

РЕЗИСТАНТ ЗАСТОСУВАННЯ ЦИКЛУ: Черги диспетчеризації - це ліцензовані об'єкти, але вам не потрібно зберігати та випускати глобальні черги, оскільки вони є глобальними, і таким чином зберігання та випуск ігнорується. Ви можете отримати доступ до глобальних черг безпосередньо, не присвоюючи їм ресурс.

Існує два способи відправки черг: синхронно та асинхронно.

SYNC DISPATCHING означає, що потік, куди була відправлена ​​черга (виклична нитка), призупиняється після відправлення черги і чекає, коли завдання в цьому блоці черги закінчить виконання перед відновленням. Щоб синхронно відправити:

DispatchQueue.global(qos: .default).sync {
    // task goes in here
}

ASYNC DISPATCHING означає, що викликова нитка продовжує працювати після відправки черги і не чекає, коли завдання в цьому блоці черги закінчить виконання. Щоб асинхронно відправити:

DispatchQueue.global(qos: .default).async {
    // task goes in here
}

Тепер можна подумати, що для того, щоб виконувати завдання послідовно, слід використовувати послідовну чергу, і це не зовсім правильно. Для того, щоб виконувати кілька завдань послідовно, слід використовувати послідовну чергу, але всі завдання (окремі самі) виконуються послідовно. Розглянемо цей приклад:

whichQueueShouldIUse.syncOrAsync {
    for i in 1...10 {
        print(i)
    }
    for i in 1...10 {
        print(i + 100)
    }
    for i in 1...10 {
        print(i + 1000)
    }
}

Незалежно від того, як ви налаштовували (послідовно чи паралельно) чи відправляли (синхронізували чи асинхронізували) цю чергу, це завдання завжди буде виконуватися послідовно. Третій цикл ніколи не запускається перед другим циклом, а другий цикл ніколи не запускається перед першим циклом. Це вірно в будь-якій черзі з використанням будь-якої відправки. Це коли ви вводите кілька завдань та / або черг, де послідовно і одночасно виникають паралельні можливості.

Розглянемо ці дві черги, одну послідовну та одну паралельну:

let serialQueue = DispatchQueue(label: "serial")
let concurrentQueue = DispatchQueue.global(qos: .default)

Скажімо, ми відправляємо дві паралельні черги в системі async:

concurrentQueue.async {
    for i in 1...5 {
        print(i)
    }
}
concurrentQueue.async {
    for i in 1...5 {
        print(i + 100)
    }
}

1
101
2
102
103
3
104
4
105
5

Їх вихід змішується (як очікувалося), але зауважте, що кожна черга виконувала своє завдання послідовно. Це найосновніший приклад одночасності - дві задачі, що працюють одночасно на задньому плані в одній черзі. Тепер давайте зробимо перший серіал:

serialQueue.async {
    for i in 1...5 {
        print(i)
    }
}
concurrentQueue.async {
    for i in 1...5 {
        print(i + 100)
    }
}

101
1
2
102
3
103
4
104
5
105

Чи не повинна перша черга виконуватися послідовно? Це було (і так було друге). Що б ще не сталося на задньому плані, це не хвилює черги. Ми сказали серійну чергу виконувати серійно, і це було ... але ми дали лише одне завдання. Тепер дамо йому два завдання:

serialQueue.async {
    for i in 1...5 {
        print(i)
    }
}
serialQueue.async {
    for i in 1...5 {
        print(i + 100)
    }
}

1
2
3
4
5
101
102
103
104
105

І це найосновніший (і єдино можливий) приклад серіалізації - два завдання, що працюють послідовно (одна за одною) на задньому плані (до головної нитки) в одній черзі. Але якщо ми створили їм дві окремі послідовні черги (адже у наведеному вище прикладі вони однакові черги), їх вихід знову змішується:

serialQueue.async {
    for i in 1...5 {
        print(i)
    }
}
serialQueue2.async {
    for i in 1...5 {
        print(i + 100)
    }
}

1
101
2
102
3
103
4
104
5
105

І це я мав на увазі, коли сказав, що всі черги паралельно одна одній. Це дві послідовні черги, що виконують свої завдання одночасно (адже це окремі черги). Черга не знає і не піклується про інші черги. Тепер повернемося до двох послідовних черг (тієї ж черги) та додамо третю чергу, паралельну:

serialQueue.async {
    for i in 1...5 {
        print(i)
    }
}
serialQueue.async {
    for i in 1...5 {
        print(i + 100)
    }
}
concurrentQueue.async {
    for i in 1...5 {
        print(i + 1000)
    }
}

1
2
3
4
5
101
102
103
104
105
1001
1002
1003
1004
1005

Це несподівано, чому паралельна черга чекала завершення послідовних черг перед її виконанням? Це не сумісність. На вашому майданчику може бути інший результат, але мій показав це. І це показало це тому, що пріоритет моєї паралельної черги був недостатньо високим, щоб GCD швидше виконала своє завдання. Тож якщо я зберігаю все те саме, але зміню QoS глобальної черги (її якість обслуговування, яка є просто пріоритетним рівнем черги) let concurrentQueue = DispatchQueue.global(qos: .userInteractive), то вихід, як очікується:

1
1001
1002
1003
2
1004
1005
3
4
5
101
102
103
104
105

Дві послідовні черги виконували свої завдання послідовно (як і очікувалося), а паралельна черга виконувала своє завдання швидше, оскільки їй було надано високий рівень пріоритетності (високий рівень якості або якість обслуговування).

Дві паралельні черги, як у нашому першому прикладі друку, показують змішану роздруківку (як очікувалося). Щоб змусити їх друкувати акуратно послідовно, нам доведеться зробити їх обох однаковою послідовною чергою (той самий екземпляр цієї черги, а не лише ту саму мітку) . Потім кожне завдання виконується послідовно щодо іншого. Інший спосіб, однак, змусити їх друкувати серійно - це тримати їх одночасно, але змінити метод відправки:

concurrentQueue.sync {
    for i in 1...5 {
        print(i)
    }
}
concurrentQueue.async {
    for i in 1...5 {
        print(i + 100)
    }
}

1
2
3
4
5
101
102
103
104
105

Пам’ятайте, що синхронізація диспетчеризації означає лише те, що викликовий потік чекає, поки завдання в черзі буде виконано, перш ніж продовжувати. Застереження тут, очевидно, полягає в тому, що виклична нитка застигає, поки не завершиться перше завдання, яке може бути, а може і не таким, як ви хочете виконати інтерфейс користувача.

І саме тому ми не можемо зробити наступне:

DispatchQueue.main.sync { ... }

Це єдине можливе поєднання черг та методів диспетчеризації, які ми не можемо виконати - синхронна диспетчеризація на головній черзі. І це тому, що ми просимо головну чергу заморозити, поки ми не виконаємо завдання в межах фігурних дужок ..., які ми відправили в основну чергу, яку ми просто заморозили. Це називається тупиком. Щоб побачити це в дії на ігровому майданчику:

DispatchQueue.main.sync { // stop the main queue and wait for the following to finish
    print("hello world") // this will never execute on the main queue because we just stopped it
}
// deadlock

Останнє, що потрібно згадати, - це ресурси. Коли ми даємо черзі завдання, GCD знаходить наявну чергу зі свого внутрішньо керованого пулу. Що стосується написання цієї відповіді, то на qos є 64 черги. Це може здатися великим, але їх можна швидко споживати, особливо сторонніми бібліотеками, особливо рамками баз даних. З цієї причини Apple має рекомендації щодо керування чергою (згадані у посиланнях нижче); одна істота:

Замість створення приватних одночасних черг, подайте завдання в одну з глобальних паралельних черг диспетчеризації. Для послідовних завдань встановіть ціль своєї послідовної черги на одну з глобальних паралельних черг. Таким чином, ви можете підтримувати серіалізовану поведінку черги, мінімізуючи кількість окремих черг, створюючи потоки.

Для цього замість того, щоб створювати їх так, як ми робили раніше (що ви все ще можете), Apple рекомендує створити такі послідовні черги:

let serialQueue = DispatchQueue(label: "serialQueue", qos: .default, attributes: [], autoreleaseFrequency: .inherit, target: .global(qos: .default))

Для подальшого читання рекомендую наступне:

https://developer.apple.com/library/archive/documentation/General/Conceptual/ConcurrencyProgrammingGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40008091-CH1-SW1

https://developer.apple.com/documentation/dispatch/dispatchqueue


7

Якщо я правильно розумію, як працює GCD, я вважаю, що існує два типи DispatchQueue, serialі concurrent, в той же час, є два способи DispatchQueueвідправлення його завдань, призначених closure, перший - asyncдругий sync. Вони разом визначають, як насправді виконується закриття (завдання).

Я знайшов це serialі concurrentозначаю, скільки ниток цієї черги може використовувати, serialозначає один, тоді як concurrentозначає багато. І , syncі asyncозначає , що завдання буде виконуватися на який потік, потік викликає або нитку , що лежить в основі цієї черги, syncзначить працювати на потоці абонента в той час як asyncзасіб запуску на основний нитки.

Далі експериментальний код, який може працювати на ігровому майданчику Xcode.

PlaygroundPage.current.needsIndefiniteExecution = true
let cq = DispatchQueue(label: "concurrent.queue", attributes: .concurrent)
let cq2 = DispatchQueue(label: "concurent.queue2", attributes: .concurrent)
let sq = DispatchQueue(label: "serial.queue")

func codeFragment() {
  print("code Fragment begin")
  print("Task Thread:\(Thread.current.description)")
  let imgURL = URL(string: "http://stackoverflow.com/questions/24058336/how-do-i-run-asynchronous-callbacks-in-playground")!
  let _ = try! Data(contentsOf: imgURL)
  print("code Fragment completed")
}

func serialQueueSync() { sq.sync { codeFragment() } }
func serialQueueAsync() { sq.async { codeFragment() } }
func concurrentQueueSync() { cq2.sync { codeFragment() } }
func concurrentQueueAsync() { cq2.async { codeFragment() } }

func tasksExecution() {
  (1...5).forEach { (_) in
    /// Using an concurrent queue to simulate concurent task executions.
    cq.async {
      print("Caller Thread:\(Thread.current.description)")
      /// Serial Queue Async, tasks run serially, because only one thread that can be used by serial queue, the underlying thread of serial queue.
      //serialQueueAsync()
      /// Serial Queue Sync, tasks run serially, because only one thread that can be used by serial queue,one by one of the callers' threads.
      //serialQueueSync()
      /// Concurrent Queue Async, tasks run concurrently, because tasks can run on different underlying threads
      //concurrentQueueAsync()
      /// Concurrent Queue Sync, tasks run concurrently, because tasks can run on different callers' thread
      //concurrentQueueSync()
    }
  }
}
tasksExecution()

Сподіваюся, це може бути корисним.


7

Мені подобається думати про це за допомогою цієї метафори (Ось посилання на оригінальне зображення):

Татові знадобиться допомога

Давайте уявимо, що ваш тато миє посуд, і ви щойно випили склянку соди. Ви приносите чашку татові, щоб його почистити, поклавши його крім іншої страви.

Тепер ваш тато самостійно робить посуд, тож йому доведеться їх робити по черзі: Ваш тато тут представляє послідовну чергу .

Але вам не дуже цікаво стояти там і спостерігати, як це прибирають. Отже, ви опускаєте склянку і повертаєтесь до своєї кімнати: це називається відправленням асинхронізації . Ваш тато може або не може повідомити вас про це, як тільки він закінчить, але важливим є те, що ви не чекаєте очищення скла; Ви повертаєтесь до своєї кімнати, щоб зробити, знаєте, дитячі речі.

Тепер давайте припустимо, що ви все ще спрагли і хочете, щоб на тому ж склянці було трохи води, яке вам подобається, і вам дуже хочеться повернути його, як тільки його очистять. Отже, ти стоїш там і спостерігаєш, як тато миє посуд, поки не закінчиться твоя. Це відправлення синхронізації , оскільки ви заблоковані, поки ви чекаєте завершення завдання.

І, нарешті, скажімо, що ваша мама вирішує допомогти вашому татові і приєднується до того, що він миє посуд. Тепер черга стає одночасною чергою, оскільки вони можуть прибирати одночасно кілька страв; але зауважте, що ви все ще можете вирішити зачекати там або повернутися до своєї кімнати, незалежно від того, як вони працюють.

Сподіваюся, це допомагає


3

1. Я читаю, що послідовні черги створюються та використовуються для виконання завдань одна за одною. Однак, що станеться, якщо: - • я створюю послідовну чергу • я тричі використовую dispatch_async (на щойно створеній послідовній черзі), щоб відправити три блоки A, B, C

ВІДПОВІДЬ : - Усі три блоки виконувались один за одним. Я створив один зразок коду, який допомагає зрозуміти.

let serialQueue = DispatchQueue(label: "SampleSerialQueue")
//Block first
serialQueue.async {
    for i in 1...10{
        print("Serial - First operation",i)
    }
}

//Block second
serialQueue.async {
    for i in 1...10{
        print("Serial - Second operation",i)
    }
}
//Block Third
serialQueue.async {
    for i in 1...10{
        print("Serial - Third operation",i)
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.