Reputation: 676
DispatchQueue.global().sync
and DispatchQueue.main.async
In a nutshell:
for index in 1…20
loop that does some "stuff."I've tried putting all the "work" on the background thread, I even created a thread with QoSClass.utility
, and no matter what I do … it completely ignores all occurrences of DispatchQueue.main.async
until the entire function has completed.
I have no problem getting done the work my code needs to accomplish. My challenge at this point is controlling the order in which things are happening and getting the display to update (AND remain responsive to the user, which is currently does NOT do).
Using the answer below … IF I use .async
I get the timely updates that I desire BUT I don't get the index results in order 1, 2, 3 … 20. IF I use .sync
I get the order that I want but NO updates. How do I get my loop to execute IN ORDER and GET UPDATES to display once per loop?
I am VERY NEW to threading here, so please type slowly. Thank you!
Here's my VERY STRIPPED DOWN code:
//
// ValidateViewController.swift
// MySpecialProject
//
// Created by Jim Termini on 10/16/21.
//
import UIKit
import PDFKit
import UniformTypeIdentifiers
import CoreData
class ValidateViewController: UIViewController, PDFViewDelegate {
@IBOutlet weak var tableView: UITableView!
var statuses: [ValidationStatus] = []
//🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠
//🟠 START HERE: ViewDidLoad
//🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠
override func viewDidLoad() {
super.viewDidLoad()
statuses = createStatusArray()
tableView.delegate = self
tableView.dataSource = self
}
//🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠
//🟠 Button Pressed — Start Validating the Document!!
//🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠
@IBAction func startValidatIn(_ sender: Any) {
print("🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢")
print("🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢")
theMainThing()
print("🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢")
print("🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢")
}
//🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠
//🟠 theMainThing function called when button pushed.
//🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠🟠
func theMainThing() {
print("🔴🟡🔴🟡🔴🟡🔴🟡🔴🟡🔴🟡🔴🟡🔴🟡🔴🟡🔴🟡🔴🟡🔴🟡🔴🟡🔴🟡🔴🟡")
print(pickedDocSourceFPnFURL ?? "No File Picked")
let openResults = openFile(pdfURLToSearch: pickedDocSourceFPnFURL!)
originalPDFDocument = openResults.0
metaPDFModifiedDate = openResults.1
var unaccountedPageCount = orginalDocumentPageCount
// Keep track of the total number of Forms found in the Document
var theFormCount = 0
let JimmyQoS: DispatchQoS.QoSClass = DispatchQoS.QoSClass.utility
let JimmysQue: DispatchQueue = DispatchQueue.global(qos: JimmyQoS)
var countOfFormsOfThisPageLength: Int = 0
var dummyVar: Int = 0
for index in 1...20 {
JimmysQue.sync {
print("🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡")
if index == 6 {
//
// Do special case stuff when index = 6
//
} else if index == 1 {
//
// Do special case stuff when index = 6
//
} else {
//
// Do regular case stuff when index = 2 – 5, 7 – 20.
//
}
print("🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴")
print("The NEXT Line is \"DispatchQueue.main.async\" SHOULD see GREEN row NEXT!!")
DispatchQueue.main.async {
print("🟢🟢🟢\(Date())🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢")
print("Here I am ... INSIDE the main thread async.")
if countOfFormsOfThisPageLength > 0 {
let term = (countOfFormsOfThisPageLength > 1) ? "forms" : "form"
self.statuses.append((ValidationStatus(image: StatusIcon.allGood, title: "Identified \(countOfFormsOfThisPageLength) \(term) of \(index) pages each.", details: "Unaccounted for Pages: \(dummyVar) of \(self.orginalDocumentPageCount)")))
self.tableView.reloadData()
self.tableView.setNeedsDisplay()
}
print("This SHOULD be causing my display to update.")
print("🟢🟢🟢\(Date())🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢")
}
print("🟣🟣🟣\(Date())🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣")
print("Here I am ... about to sleep the global (background) thread for 5 seconds to ensure the TableView is updated and displayed properly.")
sleep(5)
print("And here I am ... 5 seconds later ... still no UIViewTable Update :-(")
print("🟣🟣🟣\(Date())🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣")
}
}
}
}
Here is the walk-thru of what does and should happen…
theMainThing()
function is called ✅for index in 1…20
loop starts ✅
DispatchQueue.main.async
block is SUPPOSED to execute, at a higher priority, updating the UITableView
. ✅And this is my ACTUAL console output:
for index in 1…20
loop completes + AFTER the theMainThing()
function completes + AFTER the startValidatIn(_ sender: Any)
function completes‼️🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢
🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢
🔴🟡🔴🟡🔴🟡🔴🟡🔴🟡🔴🟡🔴🟡🔴🟡🔴🟡🔴🟡🔴🟡🔴🟡🔴🟡🔴🟡🔴🟡
🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡
🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴
The NEXT Line is "DispatchQueue.main.async" SHOULD see GREEN row NEXT!!
🟣🟣🟣2021-10-17 02:34:21 +0000🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣
Here I am ... about to sleep the global (background) thread for 5 seconds
to ensure the TableView is updated and displayed properly.
And here I am ... 5 seconds later ... still no UIViewTable Update :-(
🟣🟣🟣2021-10-17 02:34:26 +0000🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣
-------------------------
18 Duplicate Sets Omitted
-------------------------
🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡
🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴
The NEXT Line is "DispatchQueue.main.async" SHOULD see GREEN row NEXT!!
🟣🟣🟣2021-10-17 02:34:26 +0000🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣
Here I am ... about to sleep the global (background) thread for 5 seconds
to ensure the TableView is updated and displayed properly.
And here I am ... 5 seconds later ... still no UIViewTable Update :-(
🟣🟣🟣2021-10-17 02:34:31 +0000🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣
🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢
🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢
🟢🟢🟢2021-10-17 02:46:34 +0000🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢
Here I am ... INSIDE the main thread async.
This SHOULD be causing my display to update.
🟢🟢🟢2021-10-17 02:46:34 +0000🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢
🟢🟢🟢2021-10-17 02:46:34 +0000🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢
Here I am ... INSIDE the main thread async.
This SHOULD be causing my display to update.
🟢🟢🟢2021-10-17 02:46:34 +0000🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢
This is what my console output SHOULD be:
🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢
🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢
🔴🟡🔴🟡🔴🟡🔴🟡🔴🟡🔴🟡🔴🟡🔴🟡🔴🟡🔴🟡🔴🟡🔴🟡🔴🟡🔴🟡🔴🟡
🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡
🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴
The NEXT Line is "DispatchQueue.main.async" SHOULD see GREEN row NEXT!!
🟢🟢🟢2021-10-17 02:46:34 +0000🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢
Here I am ... INSIDE the main thread async.
This SHOULD be causing my display to update.
🟢🟢🟢2021-10-17 02:46:34 +0000🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢
🟣🟣🟣2021-10-17 02:34:21 +0000🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣
Here I am ... about to sleep the global (background) thread for 5 seconds
to ensure the TableView is updated and displayed properly.
And here I am ... 5 seconds later ... still no UIViewTable Update :-(
🟣🟣🟣2021-10-17 02:34:26 +0000🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣
🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡🟡
🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴
The NEXT Line is "DispatchQueue.main.async" SHOULD see GREEN row NEXT!!
🟢🟢🟢2021-10-17 02:46:34 +0000🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢
Here I am ... INSIDE the main thread async.
This SHOULD be causing my display to update.
🟢🟢🟢2021-10-17 02:46:34 +0000🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢
🟣🟣🟣2021-10-17 02:34:26 +0000🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣
Here I am ... about to sleep the global (background) thread for 5 seconds
to ensure the TableView is updated and displayed properly.
And here I am ... 5 seconds later ... still no UIViewTable Update :-(
🟣🟣🟣2021-10-17 02:34:31 +0000🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣🟣
🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢
🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢🟣🟢
Upvotes: 0
Views: 101
Reputation: 23701
When the user clicks the button, you are on the Main Thread; it's handling that button click event.
While handling that event, you then create a queue JimmysQue
and execute a synchronous operation on it with JimmysQue.sync { ... }
.
By calling sync
you are saying "I want to perform this operation on another queue, but I also want to (synchronously) wait until that other queue is done. In other words you've put the Main Thread on pause until the block you've dispatched to JimmysQue
completes.
When running that background action, JimmysQue
calls DispatchQueue.main.async
. A conceptual model you might associate with this is "send an event to the main event event loop that will run this block of code when no other events are being handled."
But remember, the Main Thread is still handling your click event! You pasused it until JimmysQue
could finish running its block. So that thing you've dispatched to the main thread won't run until your button handler finishes and your button handler is still waiting on the rest of the activity you've sent to JimmysQue
.
The simple change that I think will get you the effect you're after is to dispatch your block to JimmysQue
using async
instead of sync
.
The validation block will be sent off to run in the background. The Main Thread won't wait on it (the button click event will finish and the Main Thread will go off to handle any other events).
Then when the block on JimmysQue
finishes validating it will send the block using DispatchQueue.main.async
to be executed on the Main Thread when it has some free time.
I... typed... this... very... slowly...
Here's an even more stripped down example that demonstrates the general flow. You can paste this in a Playground. Try changing the call from async
to sync
on the .utility
queue to see how the behavior changes.
import Foundation
func theMainThing() {
print("Dispatching blocks from main thread")
for index in 1...20 {
DispatchQueue.global(qos: .utility).async {
// Sleep for a random amount of time to simulate a long
// running Action
sleep((1...5).randomElement()!)
DispatchQueue.main.async {
print("The main thing's index number \(index) completed")
}
}
}
print("The Main Thread has gone on to bigger and better things")
}
theMainThing()
In comments you asked about ordering the completion of the results. Here's an example of that:
import Foundation
func theMainThing() {
print("Dispatching Async blocks from main thread")
let scottsQueue = DispatchQueue(label: "ValidationQueue", qos: .utility)
for index in 1...20 {
scottsQueue.async {
print("The starting item \(index)")
// Sleep for a random amount of time to simulate a long
// running Action
sleep((1...5).randomElement()!)
DispatchQueue.main.sync {
print("The main thing's index number \(index) completed")
}
}
}
print("The Main Thread has gone on to bigger and better things")
}
theMainThing()
Note that we put all the validation blocks into a serial queue so they are executed strictly in order. Each block dispatches to the main queue using sync
so each background task waits for the Main Queue to acknowledge the completion before it completes.
Upvotes: 1