iOS多線程
Thread
線程的創(chuàng)建
兩種創(chuàng)建線程的方法 礼旅。常用屬性:
- name
- Thread.sleep(forTimeInterval: 5)
- Thead.current
- Thread.main
let threadA = Thread(target: self, selector: #selector(ViewController.run), object: nil)
threadA.start()
let threadB = Thread(block: <#T##() -> Void#>)
threadA.start()
Thread.detachNewThreadSelector(#selector(self.run), toTarget: self, with: nil)
class ThreadTest {
@objc func run() {
for i in 0...10 {
print("----------- curent thread = \(Thread.current.name!) number = \(i) ----------")
}
}
public func printNumbers() {
Thread.main.name = "Main"
for i in 1...5 {
let thread = Thread(target: self, selector: #selector(self.run), object: nil)
thread.name = "thread\(i)"
print("=========== curent thread = \(Thread.current.name!) child thread = \(thread.name!) ==========")
thread.start()
}
}
}
ThreadTest().printNumbers()
運(yùn)行結(jié)果 :
=========== curent thread = Main child thread = thread1 ==========
=========== curent thread = Main child thread = thread2 ==========
----------- curent thread = thread1 number = 0 ----------
----------- curent thread = thread1 number = 1 ----------
----------- curent thread = thread1 number = 2 ----------
----------- curent thread = thread1 number = 3 ----------
----------- curent thread = thread2 number = 0 ----------
----------- curent thread = thread1 number = 4 ----------
----------- curent thread = thread1 number = 5 ----------
----------- curent thread = thread1 number = 6 ----------
=========== curent thread = Main child thread = thread3 ==========
----------- curent thread = thread2 number = 1 ----------
----------- curent thread = thread2 number = 2 ----------
----------- curent thread = thread1 number = 7 ----------
----------- curent thread = thread2 number = 3 ----------
----------- curent thread = thread1 number = 8 ----------
=========== curent thread = Main child thread = thread4 ==========
----------- curent thread = thread1 number = 9 ----------
----------- curent thread = thread3 number = 0 ----------
----------- curent thread = thread2 number = 4 ----------
----------- curent thread = thread2 number = 5 ----------
----------- curent thread = thread4 number = 0 ----------
----------- curent thread = thread2 number = 6 ----------
----------- curent thread = thread1 number = 10 ----------
----------- curent thread = thread4 number = 1 ----------
----------- curent thread = thread3 number = 1 ----------
----------- curent thread = thread2 number = 7 ----------
----------- curent thread = thread3 number = 2 ----------
=========== curent thread = Main child thread = thread5 ==========
----------- curent thread = thread2 number = 8 ----------
----------- curent thread = thread2 number = 9 ----------
----------- curent thread = thread3 number = 3 ----------
----------- curent thread = thread2 number = 10 ----------
----------- curent thread = thread4 number = 2 ----------
----------- curent thread = thread3 number = 4 ----------
----------- curent thread = thread4 number = 3 ----------
----------- curent thread = thread3 number = 5 ----------
----------- curent thread = thread4 number = 4 ----------
----------- curent thread = thread5 number = 0 ----------
----------- curent thread = thread3 number = 6 ----------
----------- curent thread = thread4 number = 5 ----------
----------- curent thread = thread3 number = 7 ----------
----------- curent thread = thread4 number = 6 ----------
----------- curent thread = thread5 number = 1 ----------
----------- curent thread = thread3 number = 8 ----------
----------- curent thread = thread4 number = 7 ----------
----------- curent thread = thread5 number = 2 ----------
----------- curent thread = thread4 number = 8 ----------
----------- curent thread = thread5 number = 3 ----------
----------- curent thread = thread3 number = 9 ----------
----------- curent thread = thread4 number = 9 ----------
----------- curent thread = thread5 number = 4 ----------
----------- curent thread = thread4 number = 10 ----------
----------- curent thread = thread3 number = 10 ----------
----------- curent thread = thread5 number = 5 ----------
----------- curent thread = thread5 number = 6 ----------
----------- curent thread = thread5 number = 7 ----------
----------- curent thread = thread5 number = 8 ----------
----------- curent thread = thread5 number = 9 ----------
----------- curent thread = thread5 number = 10 ----------
線程的退出
退出條件:
- 代碼塊執(zhí)行完畢悬包,正常退出
- 執(zhí)行代碼塊出錯(cuò)魄梯,異常退出
- 調(diào)用exit()方法
主線程沒有辦法直接退出子線程,可以在主線程中給子線程發(fā)送信號(hào)(willExitThread?.cancel()),子線程代碼塊中判斷 Thread.current.isCancelled 屬性,來決定是否退出線程。
class ThreadTest {
private var willExitThread: Thread?
@objc private func exitThreadRun() {
for i in 0...100 {
if Thread.current.isCancelled {
Thread.exit()
}
print("current thread name = \(Thread.current.name!) ----> number = \(i)")
Thread.sleep(forTimeInterval: 0.5)
}
}
public func exitThread() {
willExitThread = Thread.init(target: self, selector: #selector(self.exitThreadRun), object: nil)
willExitThread?.name = "WillExitThread"
willExitThread?.start()
Thread.sleep(forTimeInterval: 5)
willExitThread?.cancel()
}
}
ThreadTest().exitThread()
current thread name = WillExitThread ----> number = 0
current thread name = WillExitThread ----> number = 1
current thread name = WillExitThread ----> number = 2
current thread name = WillExitThread ----> number = 3
current thread name = WillExitThread ----> number = 4
current thread name = WillExitThread ----> number = 5
current thread name = WillExitThread ----> number = 6
current thread name = WillExitThread ----> number = 7
current thread name = WillExitThread ----> number = 8
current thread name = WillExitThread ----> number = 9
線程的優(yōu)先級
- 默認(rèn)優(yōu)先級0.5
- 設(shè)置優(yōu)先級為0---1.0之間的數(shù)字:threadA.threadPriority = 0.01
class ThreadTest {
@objc func run() {
for i in 0...10 {
print("----------- curent thread = \(Thread.current.name!) number = \(i) ----------")
}
}
public func threadPriority() {
print("UI thread priority = \(Thread.current.threadPriority)")
let threadA = Thread(target: self, selector: #selector(self.run), object: nil)
threadA.name = "threadA"
print("ThreadA priority = \(Thread.current.threadPriority)")
threadA.threadPriority = 0.01
let threadB = Thread(target: self, selector: #selector(self.run), object: nil)
threadB.name = "threadB"
print("ThreadB priority = \(Thread.current.threadPriority)")
threadB.threadPriority = 1.0
threadA.start()
threadB.start()
}
}
ThreadTest().threadPriority()
UI thread priority = 0.5
ThreadA priority = 0.5
ThreadB priority = 0.5
----------- curent thread = threadB number = 0 ----------
----------- curent thread = threadB number = 1 ----------
----------- curent thread = threadB number = 2 ----------
----------- curent thread = threadA number = 0 ----------
----------- curent thread = threadB number = 3 ----------
----------- curent thread = threadB number = 4 ----------
----------- curent thread = threadB number = 5 ----------
----------- curent thread = threadA number = 1 ----------
----------- curent thread = threadB number = 6 ----------
----------- curent thread = threadB number = 7 ----------
----------- curent thread = threadB number = 8 ----------
----------- curent thread = threadA number = 2 ----------
----------- curent thread = threadB number = 9 ----------
----------- curent thread = threadB number = 10 ----------
----------- curent thread = threadA number = 3 ----------
----------- curent thread = threadA number = 4 ----------
----------- curent thread = threadA number = 5 ----------
----------- curent thread = threadA number = 6 ----------
----------- curent thread = threadA number = 7 ----------
----------- curent thread = threadA number = 8 ----------
----------- curent thread = threadA number = 9 ----------
----------- curent thread = threadA number = 10 ----------
線程同步
線程安全類的特點(diǎn):
- 類的實(shí)例可以被多個(gè) 線程安全的訪問
- 線程調(diào)用該對象任何方法之后獲得正確的結(jié)果
- 線程調(diào)用對象的任何方法计寇,對象保持合理狀態(tài)
不可變類總是線程安全的,可變類則需要提供額外的方法保證其線程安全:
- 對會(huì)改變共享資源的方法進(jìn)行線程同步
- 區(qū)分運(yùn)行環(huán)境,提供線程安全版本和線程不安全版本番宁。
objc_sync_enter(self)
objc_sync_exit(self)
let lock = NSLock()
lock.lock()
lock.unlock()
class Account {
var accountNumber: String
var balance: Double
var lock: NSLock!
init(_ accountNumber: String, _ balance : Double) {
self.accountNumber = accountNumber
self.balance = balance
self.lock = NSLock()
}
func draw(_ drawAmmount : Double) {
// objc_sync_enter(self)
// lock.lock()
if self.balance > drawAmmount {
print("Thread name = \(Thread.current.name ?? "" ) sucess to draw : \(drawAmmount)")
Thread.sleep(forTimeInterval: 0.1)
self.balance -= drawAmmount
print("Thread name = \(Thread.current.name ?? "" ) balance = \(self.balance)")
} else {
print("Thread name = \(Thread.current.name ?? "" ) failed to draw because of insufficient founds")
}
// lock.unlock()
// objc_sync_exit(self)
}
}
class ThreadTest {
private var account = Account("123456", 1000)
public func draw() {
let thread1 = Thread(target: self, selector: #selector(self.drawMoneyFromAccount(money:)), object: 800.0)
thread1.name = "thread1"
let thread2 = Thread(target: self, selector: #selector(self.drawMoneyFromAccount(money:)), object: 800.0)
thread2.name = "threa2"
thread1.start()
thread2.start()
}
@objc private func drawMoneyFromAccount(money : NSNumber) {
account.draw(money.doubleValue)
}
}
Thread name = thread2 sucess to draw : 800.0
Thread name = thread1 sucess to draw : 800.0
Thread name = thread2 balance = 200.0
Thread name = thread1 balance = -600.0
Thread name = thread1 sucess to draw : 800.0
Thread name = thread1 balance = 200.0
Thread name = thread2 failed to draw because of insufficient founds
線程間通信
iOS提供NSCondition類來提供控制線程通信元莫。其遵守NSLocking協(xié)議也可以用來同步。
- wait()方法:當(dāng)前線程等待蝶押,直到被喚醒
- singal()方法:任意喚醒一個(gè)在等待的線程
- broadcast()方法:喚醒所有等待線程
class Account {
var accountNumber: String
var balance: Double
var lock: NSLock!
init(_ accountNumber: String, _ balance : Double) {
self.accountNumber = accountNumber
self.balance = balance
self.lock = NSLock()
}
func draw(_ drawAmmount : Double) {
// objc_sync_enter(self)
lock.lock()
if self.balance > drawAmmount {
print("Thread name = \(Thread.current.name ?? "" ) sucess to draw : \(drawAmmount)")
Thread.sleep(forTimeInterval: 0.1)
self.balance -= drawAmmount
print("Thread name = \(Thread.current.name ?? "" ) balance = \(self.balance)")
} else {
print("Thread name = \(Thread.current.name ?? "" ) failed to draw because of insufficient founds")
}
lock.unlock()
// objc_sync_exit(self)
}
}
class SavingAccount : Account {
var condition : NSCondition!
var flag : Bool = true
override init(_ accountNumber: String, _ balance: Double) {
self.condition = NSCondition()
super.init(accountNumber, balance)
}
override func draw(_ drawAmmount: Double) {
condition.lock()
if !flag {
condition.wait()
} else {
self.balance -= drawAmmount
print("Thread name = \(Thread.current.name ?? "" ) sucess to draw : \(drawAmmount) balance = \(self.balance)")
flag = false
condition.broadcast()
}
condition.unlock()
}
func deposit(money : Double) {
condition.lock()
if flag {
condition.wait()
} else {
self.balance += money
print("Thread name = \(Thread.current.name ?? "" ) sucess to deposit : \(money) balance = \(self.balance)")
flag = true
condition.broadcast()
}
condition.unlock()
}
}
class ThreadTest {
let savingAccount = SavingAccount("123456", 1000.0)
public func depositThenDraw() {
for i in 0..<3 {
let thread = Thread(target: self, selector: #selector(self.depositMoneyMethod(money:)), object: 800)
thread.name = "Deposit thread\(i)"
thread.start()
}
let thread = Thread(target: self, selector: #selector(self.drawMoneyMethod(money:)), object: 800)
thread.name = "draw thread"
thread.start()
}
@objc private func depositMoneyMethod(money: NSNumber) {
for _ in 0..<10 {
savingAccount.deposit(money: money.doubleValue)
}
}
@objc private func drawMoneyMethod(money: NSNumber) {
for _ in 0..<30 {
savingAccount.draw(money.doubleValue)
}
}
}
ThreadTest().depositThenDraw()
Thread name = draw thread sucess to draw : 800.0 balance = 200.0
Thread name = Deposit thread0 sucess to deposit : 800.0 balance = 1000.0
Thread name = draw thread sucess to draw : 800.0 balance = 200.0
Thread name = Deposit thread1 sucess to deposit : 800.0 balance = 1000.0
Thread name = draw thread sucess to draw : 800.0 balance = 200.0
Thread name = Deposit thread1 sucess to deposit : 800.0 balance = 1000.0
Thread name = draw thread sucess to draw : 800.0 balance = 200.0
Thread name = Deposit thread1 sucess to deposit : 800.0 balance = 1000.0
Thread name = draw thread sucess to draw : 800.0 balance = 200.0
Thread name = Deposit thread1 sucess to deposit : 800.0 balance = 1000.0
Thread name = draw thread sucess to draw : 800.0 balance = 200.0
Thread name = Deposit thread1 sucess to deposit : 800.0 balance = 1000.0
Thread name = draw thread sucess to draw : 800.0 balance = 200.0
Thread name = Deposit thread1 sucess to deposit : 800.0 balance = 1000.0
Thread name = draw thread sucess to draw : 800.0 balance = 200.0
Thread name = Deposit thread1 sucess to deposit : 800.0 balance = 1000.0
Thread name = draw thread sucess to draw : 800.0 balance = 200.0
Thread name = Deposit thread0 sucess to deposit : 800.0 balance = 1000.0
Thread name = draw thread sucess to draw : 800.0 balance = 200.0
Thread name = Deposit thread2 sucess to deposit : 800.0 balance = 1000.0
Thread name = draw thread sucess to draw : 800.0 balance = 200.0
Thread name = Deposit thread0 sucess to deposit : 800.0 balance = 1000.0
Thread name = draw thread sucess to draw : 800.0 balance = 200.0
NSOperation
可以將Operation理解為任務(wù)操作踱蠢,OperationQueue理解為操作隊(duì)列。
open class Operation : NSObject {
open func start()
open func main()
open var isCancelled: Bool { get }
open func cancel()
open var isExecuting: Bool { get }
open var isFinished: Bool { get }
open var isConcurrent: Bool { get }
@available(iOS 7.0, *)
open var isAsynchronous: Bool { get }
open var isReady: Bool { get }
open func addDependency(_ op: Operation)
open func removeDependency(_ op: Operation)
open var dependencies: [Operation] { get }
open var queuePriority: Operation.QueuePriority
@available(iOS 4.0, *)
open var completionBlock: (() -> Void)?
@available(iOS 4.0, *)
open func waitUntilFinished()
@available(iOS, introduced: 4.0, deprecated: 8.0, message: "Not supported")
open var threadPriority: Double
@available(iOS 8.0, *)
open var qualityOfService: QualityOfService
@available(iOS 8.0, *)
open var name: String?
}
extension Operation {
public enum QueuePriority : Int {
case veryLow
case low
case normal
case high
case veryHigh
}
}
Operation是基于OC封裝的一套管理與執(zhí)行線程操作的類棋电。Operation是抽象類茎截,BlockOperation是其子類,我們也可以繼承Operation封裝自己的操作類赶盔。每個(gè)單獨(dú)的operation有四種狀態(tài)企锌,Ready、Executing于未、Cancelled撕攒、Finished.
class ImageeFilterOperation: Operation {
var inputImage: UIImage?
var outputImage: UIImage?
override func main() {
outputImage = filter(image: inputImage)
}
}
blockOperation
BlockOperation是Operation的子類,可以異步執(zhí)行多個(gè)代碼塊烘浦,當(dāng)所有Block都完成抖坪,操作才算完成 。
class OperationTest {
public func blockOperationDemo() {
let operation = BlockOperation {
for i in 0...10 {
print("Thread = \(Thread.current) i = \(i)")
}
}
operation.addExecutionBlock {
for j in 0...10 {
print("Thread = \(Thread.current) j = \(j)")
}
}
let operation2 = BlockOperation {
for k in 0...10 {
print("Thread = \(Thread.current) k = \(k)")
}
}
operation.start()
operation2.start()
}
}
OperationTest().blockOperationDemo()
Thread = <NSThread: 0x600000221640>{number = 1, name = main} i = 0
Thread = <NSThread: 0x6000002329c0>{number = 3, name = (null)} j = 0
Thread = <NSThread: 0x600000221640>{number = 1, name = main} i = 1
Thread = <NSThread: 0x6000002329c0>{number = 3, name = (null)} j = 1
Thread = <NSThread: 0x600000221640>{number = 1, name = main} i = 2
Thread = <NSThread: 0x6000002329c0>{number = 3, name = (null)} j = 2
Thread = <NSThread: 0x600000221640>{number = 1, name = main} i = 3
Thread = <NSThread: 0x6000002329c0>{number = 3, name = (null)} j = 3
Thread = <NSThread: 0x600000221640>{number = 1, name = main} i = 4
Thread = <NSThread: 0x6000002329c0>{number = 3, name = (null)} j = 4
Thread = <NSThread: 0x600000221640>{number = 1, name = main} i = 5
Thread = <NSThread: 0x6000002329c0>{number = 3, name = (null)} j = 5
Thread = <NSThread: 0x600000221640>{number = 1, name = main} i = 6
Thread = <NSThread: 0x6000002329c0>{number = 3, name = (null)} j = 6
Thread = <NSThread: 0x600000221640>{number = 1, name = main} i = 7
Thread = <NSThread: 0x6000002329c0>{number = 3, name = (null)} j = 7
Thread = <NSThread: 0x600000221640>{number = 1, name = main} i = 8
Thread = <NSThread: 0x6000002329c0>{number = 3, name = (null)} j = 8
Thread = <NSThread: 0x600000221640>{number = 1, name = main} i = 9
Thread = <NSThread: 0x6000002329c0>{number = 3, name = (null)} j = 9
Thread = <NSThread: 0x600000221640>{number = 1, name = main} i = 10
Thread = <NSThread: 0x6000002329c0>{number = 3, name = (null)} j = 10
Thread = <NSThread: 0x600000221640>{number = 1, name = main} k = 0
Thread = <NSThread: 0x600000221640>{number = 1, name = main} k = 1
Thread = <NSThread: 0x600000221640>{number = 1, name = main} k = 2
Thread = <NSThread: 0x600000221640>{number = 1, name = main} k = 3
Thread = <NSThread: 0x600000221640>{number = 1, name = main} k = 4
Thread = <NSThread: 0x600000221640>{number = 1, name = main} k = 5
Thread = <NSThread: 0x600000221640>{number = 1, name = main} k = 6
Thread = <NSThread: 0x600000221640>{number = 1, name = main} k = 7
Thread = <NSThread: 0x600000221640>{number = 1, name = main} k = 8
Thread = <NSThread: 0x600000221640>{number = 1, name = main} k = 9
Thread = <NSThread: 0x600000221640>{number = 1, name = main} k = 10
明顯可以看出:兩個(gè)循環(huán)在不同的線程中執(zhí)行闷叉。使用BlockOperation的最大好處:不需要顯式的操作Thread來管理線程擦俐,BlockOperation會(huì)根據(jù)加入的Block來分配線程,使之異步執(zhí)行片习。但是兩個(gè)Operation之間卻是都是在主線程中按照順序執(zhí)行的捌肴。
操作依賴關(guān)系
操作之間通常存在這一些依賴關(guān)系蹬叭。比如用網(wǎng)絡(luò)請求到的數(shù)據(jù)來渲染界面藕咏,界面的渲染一定會(huì)在請求完成之后執(zhí)行 。
class OperationTest {
public func operationDependenciesDemo() {
let operation1 = BlockOperation {
for i in 0...10 {
print("Thread = \(Thread.current) i = \(i)")
}
}
let operation2 = BlockOperation {
for i in 11...20 {
print("Thread = \(Thread.current) i = \(i)")
}
}
let queue = OperationQueue.init()
// operation2.addDependency(operation1)
// queue.maxConcurrentOperationCount = 1
queue.addOperation(operation1)
queue.addOperation(operation2)
}
}
OperationTest().operationDependenciesDemo()
Thread = <NSThread: 0x600000a20440>{number = 4, name = (null)} i = 0
Thread = <NSThread: 0x600000a3d500>{number = 3, name = (null)} i = 11
Thread = <NSThread: 0x600000a3d500>{number = 3, name = (null)} i = 12
Thread = <NSThread: 0x600000a20440>{number = 4, name = (null)} i = 1
Thread = <NSThread: 0x600000a3d500>{number = 3, name = (null)} i = 13
Thread = <NSThread: 0x600000a20440>{number = 4, name = (null)} i = 2
Thread = <NSThread: 0x600000a3d500>{number = 3, name = (null)} i = 14
Thread = <NSThread: 0x600000a20440>{number = 4, name = (null)} i = 3
Thread = <NSThread: 0x600000a3d500>{number = 3, name = (null)} i = 15
Thread = <NSThread: 0x600000a20440>{number = 4, name = (null)} i = 4
Thread = <NSThread: 0x600000a3d500>{number = 3, name = (null)} i = 16
Thread = <NSThread: 0x600000a20440>{number = 4, name = (null)} i = 5
Thread = <NSThread: 0x600000a3d500>{number = 3, name = (null)} i = 17
Thread = <NSThread: 0x600000a3d500>{number = 3, name = (null)} i = 18
Thread = <NSThread: 0x600000a20440>{number = 4, name = (null)} i = 6
Thread = <NSThread: 0x600000a3d500>{number = 3, name = (null)} i = 19
Thread = <NSThread: 0x600000a20440>{number = 4, name = (null)} i = 7
Thread = <NSThread: 0x600000a3d500>{number = 3, name = (null)} i = 20
Thread = <NSThread: 0x600000a20440>{number = 4, name = (null)} i = 8
Thread = <NSThread: 0x600000a20440>{number = 4, name = (null)} i = 9
Thread = <NSThread: 0x600000a20440>{number = 4, name = (null)} i = 10
運(yùn)行結(jié)果如上秽五,因?yàn)镺perationQueue默認(rèn)并行執(zhí)行Operation.如果我們想按順序打印0--20孽查,解決方法是添加依賴:operation2.addDependency(operation1),這樣在operation1完成之后才會(huì)執(zhí)行operation2.
OperationQueue
負(fù)責(zé)安排和運(yùn)行多個(gè)Operation坦喘,單并不局限于FIFO隊(duì)列操作,他提供多個(gè)接口實(shí)現(xiàn)暫停盲再、繼續(xù)、終止瓣铣、優(yōu)先級答朋、依賴等復(fù)雜操作。默認(rèn)并行執(zhí)行已經(jīng)加入的Operation,但是可以通過:
queue.maxConcurrentOperationCount = 1
來修改其為串行隊(duì)列棠笑。
GCD(Grand Central Dispatch)
GCD是基于c語言的多線程開發(fā)框架梦碗,相比Operation和Thread,GCD更加高效,并且線程由系統(tǒng)管理洪规,會(huì)自動(dòng)啟用多核運(yùn)算印屁。GCD是官方推薦的多線程解決方案。
調(diào)度隊(duì)列
GCD有一個(gè)重要概念調(diào)度隊(duì)列斩例。我們對線程的操作實(shí)際上由調(diào)度隊(duì)列完成雄人,我們只需要把要執(zhí)行的任務(wù)添加到合適的調(diào)度隊(duì)列中即可。
- 主隊(duì)列:任務(wù)在主線程中執(zhí)行念赶,會(huì)阻塞線程础钠,是一個(gè)串行隊(duì)列。
- 全局并行隊(duì)列叉谜。隊(duì)列中的任務(wù)按照先進(jìn)先出順序執(zhí)行:串行隊(duì)列則一個(gè)任務(wù)結(jié)束才會(huì)開啟另一個(gè)任務(wù)珍坊,并行隊(duì)列則任務(wù)開啟順序和任務(wù)添加順序一致。系統(tǒng)自動(dòng)創(chuàng)建4個(gè)全局共享并行隊(duì)列正罢。
- 自定義隊(duì)列:包括串行的和并行的阵漏。
let mainQueue = DispatchQueue.main
print(mainQueue.debugDescription)
var qos: [DispatchQoS.QoSClass] = [.background,.userInitiated,.unspecified,.userInteractive,.default,.unspecified]
for i in 0..<qos.count {
let queue = DispatchQueue.global(qos: qos[i])
print(queue.debugDescription)
}
let myQueue = DispatchQueue(label: "self-define queue", qos: DispatchQoS.default, attributes: [DispatchQueue.Attributes.concurrent], autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.inherit, target: nil)
print(myQueue.debugDescription)
<OS_dispatch_queue_main: com.apple.main-thread[0x111033a80] = { xref = -2147483648, ref = -2147483648, sref = 1, target = com.apple.root.default-qos.overcommit[0x111033f00], width = 0x1, state = 0x001ffe9d00000300, dirty, max qos 5, in-flight = 0, thread = 0x303 }>
<OS_dispatch_queue_global: com.apple.root.background-qos[0x111033c80] = { xref = -2147483648, ref = -2147483648, sref = 1, target = [0x0], width = 0xfff, state = 0x0060000000000000, in-barrier}>
<OS_dispatch_queue_global: com.apple.root.user-initiated-qos[0x111033f80] = { xref = -2147483648, ref = -2147483648, sref = 1, target = [0x0], width = 0xfff, state = 0x0060000000000000, in-barrier}>
<OS_dispatch_queue_global: com.apple.root.default-qos[0x111033e80] = { xref = -2147483648, ref = -2147483648, sref = 1, target = [0x0], width = 0xfff, state = 0x0060000000000000, in-barrier}>
<OS_dispatch_queue_global: com.apple.root.user-interactive-qos[0x111034080] = { xref = -2147483648, ref = -2147483648, sref = 1, target = [0x0], width = 0xfff, state = 0x0060000000000000, in-barrier}>
<OS_dispatch_queue_global: com.apple.root.default-qos[0x111033e80] = { xref = -2147483648, ref = -2147483648, sref = 1, target = [0x0], width = 0xfff, state = 0x0060000000000000, in-barrier}>
<OS_dispatch_queue_global: com.apple.root.default-qos[0x111033e80] = { xref = -2147483648, ref = -2147483648, sref = 1, target = [0x0], width = 0xfff, state = 0x0060000000000000, in-barrier}>
<OS_dispatch_queue_concurrent: self-define queue[0x600000691280] = { xref = 2, ref = 1, sref = 1, target = com.apple.root.default-qos[0x111033e80], width = 0xffe, state = 0x0000041000000000, in-flight = 0}>
使用隊(duì)列運(yùn)行任務(wù)
開發(fā)者自定義隊(duì)列的是時(shí)候用attributes參數(shù)控制要?jiǎng)?chuàng)建串行隊(duì)列還是并行隊(duì)列。
class DispatchQueueTest {
public func dispatchQueueDemo() {
// let serialQueue = DispatchQueue.init(label: "serial", qos: DispatchQoS.default, attributes: [], autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.inherit, target: nil)
// serialQueue.async {
// for i in 1...5 {
// print("\(Thread.current) i = \(i)")
// }
// }
// serialQueue.async {
// for i in 1...5 {
// print("\(Thread.current) i = \(i)")
// }
// }
let concurrentQueue = DispatchQueue.init(label: "concurrent", qos: DispatchQoS.default, attributes: [.concurrent], autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.inherit, target: nil)
concurrentQueue.async {
for i in 1...5 {
print("\(Thread.current) i = \(i)")
}
}
concurrentQueue.async {
for i in 1...5 {
print("\(Thread.current) i = \(i)")
}
}
}
}
DispatchQueueTest().dispatchQueueDemo()
并行隊(duì)列打印結(jié)果:
<NSThread: 0x6000028bfa00>{number = 4, name = (null)} i = 1
<NSThread: 0x6000028bfa40>{number = 5, name = (null)} i = 1
<NSThread: 0x6000028bfa00>{number = 4, name = (null)} i = 2
<NSThread: 0x6000028bfa40>{number = 5, name = (null)} i = 2
<NSThread: 0x6000028bfa00>{number = 4, name = (null)} i = 3
<NSThread: 0x6000028bfa40>{number = 5, name = (null)} i = 3
<NSThread: 0x6000028bfa00>{number = 4, name = (null)} i = 4
<NSThread: 0x6000028bfa40>{number = 5, name = (null)} i = 4
<NSThread: 0x6000028bfa40>{number = 5, name = (null)} i = 5
<NSThread: 0x6000028bfa00>{number = 4, name = (null)} i = 5
串行隊(duì)列打印結(jié)果如下翻具,并且只有一個(gè)線程工作履怯。
<NSThread: 0x6000036d09c0>{number = 3, name = (null)} i = 1
<NSThread: 0x6000036d09c0>{number = 3, name = (null)} i = 2
<NSThread: 0x6000036d09c0>{number = 3, name = (null)} i = 3
<NSThread: 0x6000036d09c0>{number = 3, name = (null)} i = 4
<NSThread: 0x6000036d09c0>{number = 3, name = (null)} i = 5
<NSThread: 0x6000036d09c0>{number = 3, name = (null)} i = 1
<NSThread: 0x6000036d09c0>{number = 3, name = (null)} i = 2
<NSThread: 0x6000036d09c0>{number = 3, name = (null)} i = 3
<NSThread: 0x6000036d09c0>{number = 3, name = (null)} i = 4
<NSThread: 0x6000036d09c0>{number = 3, name = (null)} i = 5
Sync/Async
Sync: 當(dāng)前任務(wù)加入到隊(duì)列當(dāng)中,等到任務(wù)完成裆泳,線程返回繼續(xù)運(yùn)行叹洲,即會(huì)阻塞線程。
Async:把任務(wù)加入隊(duì)列工禾,立即返回运提,無須等任務(wù)執(zhí)行完成。即不會(huì)阻塞線程 闻葵。
串行隊(duì)列和并行隊(duì)列民泵,都可以執(zhí)行同步任務(wù)和異步任務(wù)。
class DispatchQueueTest {
public func serialQueueAsyncTasks() {
let serialQueue = DispatchQueue.init(label: "serial", qos: DispatchQoS.default, attributes: [], autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.inherit, target: nil)
print("SerialQueue------>SyncTasks")
serialQueue.sync {
for i in 1...5 {
print("\(Thread.current) i = \(i)")
} }
print("-------- \(Thread.current) ----------")
serialQueue.sync {
for i in 6...10 {
print("\(Thread.current) i = \(i)")
}
}
print("-------- \(Thread.current) ----------")
print("SerialQueue------>AsyncTasks")
serialQueue.async {
for i in 1...5 {
print("\(Thread.current) i = \(i)")
}
}
print("-------- \(Thread.current) ----------")
serialQueue.async {
for i in 6...10 {
print("\(Thread.current) i = \(i)")
}
}
print("-------- \(Thread.current) ----------")
}
}
SerialQueue------>SyncTasks
<NSThread: 0x6000001dd5c0>{number = 1, name = main} i = 1
<NSThread: 0x6000001dd5c0>{number = 1, name = main} i = 2
<NSThread: 0x6000001dd5c0>{number = 1, name = main} i = 3
<NSThread: 0x6000001dd5c0>{number = 1, name = main} i = 4
<NSThread: 0x6000001dd5c0>{number = 1, name = main} i = 5
-------- <NSThread: 0x6000001dd5c0>{number = 1, name = main} ----------
<NSThread: 0x6000001dd5c0>{number = 1, name = main} i = 6
<NSThread: 0x6000001dd5c0>{number = 1, name = main} i = 7
<NSThread: 0x6000001dd5c0>{number = 1, name = main} i = 8
<NSThread: 0x6000001dd5c0>{number = 1, name = main} i = 9
<NSThread: 0x6000001dd5c0>{number = 1, name = main} i = 10
-------- <NSThread: 0x6000001dd5c0>{number = 1, name = main} ----------
SerialQueue------>AsyncTasks
-------- <NSThread: 0x6000001dd5c0>{number = 1, name = main} ----------
<NSThread: 0x6000001f4180>{number = 3, name = (null)} i = 1
-------- <NSThread: 0x6000001dd5c0>{number = 1, name = main} ----------
<NSThread: 0x6000001f4180>{number = 3, name = (null)} i = 2
<NSThread: 0x6000001f4180>{number = 3, name = (null)} i = 3
<NSThread: 0x6000001f4180>{number = 3, name = (null)} i = 4
<NSThread: 0x6000001f4180>{number = 3, name = (null)} i = 5
<NSThread: 0x6000001f4180>{number = 3, name = (null)} i = 6
<NSThread: 0x6000001f4180>{number = 3, name = (null)} i = 7
<NSThread: 0x6000001f4180>{number = 3, name = (null)} i = 8
<NSThread: 0x6000001f4180>{number = 3, name = (null)} i = 9
<NSThread: 0x6000001f4180>{number = 3, name = (null)} i = 10
class DispatchQueueTest {
public func concurrentQueueAsyncTasks() {
let concurrentQueue = DispatchQueue.init(label: "concurrent", qos: DispatchQoS.default, attributes:[.concurrent], autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.inherit, target: nil)
print("concurrentQueue------>SyncTasks")
concurrentQueue.sync {
for i in 1...5 {
print("\(Thread.current) i = \(i)")
} }
print("-------- \(Thread.current) ----------")
concurrentQueue.sync {
for i in 6...10 {
print("\(Thread.current) i = \(i)")
}
}
print("-------- \(Thread.current) ----------")
print("concurrentQueue------>AsyncTasks")
concurrentQueue.async {
for i in 1...5 {
print("\(Thread.current) i = \(i)")
}
}
print("-------- \(Thread.current) ----------")
concurrentQueue.async {
for i in 6...10 {
print("\(Thread.current) i = \(i)")
}
}
print("-------- \(Thread.current) ----------")
}
}
concurrentQueue------>SyncTasks
<NSThread: 0x60000119e8c0>{number = 1, name = main} i = 1
<NSThread: 0x60000119e8c0>{number = 1, name = main} i = 2
<NSThread: 0x60000119e8c0>{number = 1, name = main} i = 3
<NSThread: 0x60000119e8c0>{number = 1, name = main} i = 4
<NSThread: 0x60000119e8c0>{number = 1, name = main} i = 5
-------- <NSThread: 0x60000119e8c0>{number = 1, name = main} ----------
<NSThread: 0x60000119e8c0>{number = 1, name = main} i = 6
<NSThread: 0x60000119e8c0>{number = 1, name = main} i = 7
<NSThread: 0x60000119e8c0>{number = 1, name = main} i = 8
<NSThread: 0x60000119e8c0>{number = 1, name = main} i = 9
<NSThread: 0x60000119e8c0>{number = 1, name = main} i = 10
-------- <NSThread: 0x60000119e8c0>{number = 1, name = main} ----------
concurrentQueue------>AsyncTasks
-------- <NSThread: 0x60000119e8c0>{number = 1, name = main} ----------
<NSThread: 0x6000011b10c0>{number = 3, name = (null)} i = 1
-------- <NSThread: 0x60000119e8c0>{number = 1, name = main} ----------
<NSThread: 0x600001188040>{number = 4, name = (null)} i = 6
<NSThread: 0x6000011b10c0>{number = 3, name = (null)} i = 2
<NSThread: 0x600001188040>{number = 4, name = (null)} i = 7
<NSThread: 0x6000011b10c0>{number = 3, name = (null)} i = 3
<NSThread: 0x6000011b10c0>{number = 3, name = (null)} i = 4
<NSThread: 0x600001188040>{number = 4, name = (null)} i = 8
<NSThread: 0x6000011b10c0>{number = 3, name = (null)} i = 5
<NSThread: 0x600001188040>{number = 4, name = (null)} i = 9
<NSThread: 0x600001188040>{number = 4, name = (null)} i = 10
根據(jù)上述結(jié)果:由于同步任務(wù)會(huì)阻塞線程槽畔,所以系統(tǒng)不會(huì)新建線程來執(zhí)行開發(fā)者所添加的任務(wù)栈妆,例子中不管串行隊(duì)列還是并行隊(duì)列,都是number = 1的線程厢钧。通俗地講鳞尔,反正要等待,閑著也是閑著早直,不如活都讓你干了寥假。異步任務(wù)不會(huì)阻塞線程,所以添加完任務(wù)之后都會(huì)快速返回number = 1的線程霞扬。這個(gè)時(shí)候體現(xiàn)出了串行隊(duì)列和并行隊(duì)列的區(qū)別:串行隊(duì)列知會(huì)新建一個(gè)線程(number = 3)來執(zhí)行開發(fā)者添加的任務(wù)糕韧,而并行隊(duì)列的話拾给,系統(tǒng)會(huì)根據(jù)需要?jiǎng)?chuàng)建多個(gè)線程(number = 3 && number = 4)來執(zhí)行開發(fā)者添加的任務(wù)。
隊(duì)列組
class DispatchGroupTest {
public func dispatchGroupDemo() {
let group = DispatchGroup.init()
let queue = DispatchQueue.init(label: "my dispatch queue", qos: DispatchQoS.default, attributes: [.concurrent], autoreleaseFrequency: .inherit, target: nil)
//notify
queue.async(group: group, qos: DispatchQoS.default, flags: []) {
for i in 1...5 {
print("\(Thread.current) i = \(i)")
}
}
queue.async(group: group, qos: DispatchQoS.default, flags: []) {
for i in 6...10 {
print("\(Thread.current) i = \(i)")
}
}
group.notify(queue: .main) {
for i in 11...15 {
print("\(Thread.current) i = \(i)")
}
}
//wait
queue.async(group: group, qos: DispatchQoS.default, flags: []) {
print("\(Thread.current) Time-consuming task one")
Thread.sleep(forTimeInterval: 2)
}
queue.async(group: group, qos: DispatchQoS.default, flags: []) {
print("\(Thread.current) Time-consuming task two")
Thread.sleep(forTimeInterval: 2)
}
let result = group.wait(timeout: DispatchTime.now() + 5)
switch result {
case .success:
print("Wonderful! Finished two tasks!")
case .timedOut:
print("Timeout! Failed to excute last two tasks!")
}
//enter && leave
group.enter()
queue.async(group: group, qos: DispatchQoS.default, flags: []) {
print("\(Thread.current) Time-consuming task one")
for i in 1...5 {
print("\(Thread.current) i = \(i)")
}
group.leave()
}
group.enter()
queue.async(group: group, qos: DispatchQoS.default, flags: []) {
print("\(Thread.current) Time-consuming task two")
for i in 6...10 {
print("\(Thread.current) i = \(i)")
}
group.leave()
}
group.notify(queue: .main) {
print("\(Thread.current) Time-consuming task three")
for i in 11...15 {
print("\(Thread.current) i = \(i)")
}
}
}
}
DispatchGroupTest().dispatchGroupDemo()
三段代碼分別的運(yùn)行結(jié)果:
<NSThread: 0x600001a95e40>{number = 3, name = (null)} i = 1
<NSThread: 0x600001a9da40>{number = 4, name = (null)} i = 6
<NSThread: 0x600001a9da40>{number = 4, name = (null)} i = 7
<NSThread: 0x600001a95e40>{number = 3, name = (null)} i = 2
<NSThread: 0x600001a9da40>{number = 4, name = (null)} i = 8
<NSThread: 0x600001a95e40>{number = 3, name = (null)} i = 3
<NSThread: 0x600001a9da40>{number = 4, name = (null)} i = 9
<NSThread: 0x600001a95e40>{number = 3, name = (null)} i = 4
<NSThread: 0x600001a9da40>{number = 4, name = (null)} i = 10
<NSThread: 0x600001a95e40>{number = 3, name = (null)} i = 5
<NSThread: 0x600001aba800>{number = 1, name = main} i = 11
<NSThread: 0x600001aba800>{number = 1, name = main} i = 12
<NSThread: 0x600001aba800>{number = 1, name = main} i = 13
<NSThread: 0x600001aba800>{number = 1, name = main} i = 14
<NSThread: 0x600001aba800>{number = 1, name = main} i = 15
<NSThread: 0x6000033dafc0>{number = 3, name = (null)} Time-consuming task one
<NSThread: 0x6000033d41c0>{number = 4, name = (null)} Time-consuming task two
Wonderful! Finished two tasks!
<NSThread: 0x600003d0f400>{number = 3, name = (null)} Time-consuming task one
<NSThread: 0x600003d35740>{number = 4, name = (null)} Time-consuming task two
<NSThread: 0x600003d35740>{number = 4, name = (null)} i = 6
<NSThread: 0x600003d0f400>{number = 3, name = (null)} i = 1
<NSThread: 0x600003d35740>{number = 4, name = (null)} i = 7
<NSThread: 0x600003d0f400>{number = 3, name = (null)} i = 2
<NSThread: 0x600003d35740>{number = 4, name = (null)} i = 8
<NSThread: 0x600003d35740>{number = 4, name = (null)} i = 9
<NSThread: 0x600003d0f400>{number = 3, name = (null)} i = 3
<NSThread: 0x600003d0f400>{number = 3, name = (null)} i = 4
<NSThread: 0x600003d35740>{number = 4, name = (null)} i = 10
<NSThread: 0x600003d0f400>{number = 3, name = (null)} i = 5
<NSThread: 0x600003d1e500>{number = 1, name = main} Time-consuming task three
<NSThread: 0x600003d1e500>{number = 1, name = main} i = 11
<NSThread: 0x600003d1e500>{number = 1, name = main} i = 12
<NSThread: 0x600003d1e500>{number = 1, name = main} i = 13
<NSThread: 0x600003d1e500>{number = 1, name = main} i = 14
<NSThread: 0x600003d1e500>{number = 1, name = main} i = 15
GCD處理循環(huán)任務(wù)
根據(jù)打印結(jié)果兔沃,我的電腦大約有70個(gè)線程來執(zhí)行代碼塊蒋得。
let queue = DispatchQueue.global()
for i in 0...1000 {
queue.async {
print("\(Thread.current) Time-consuming task \(i)")
Thread.sleep(forTimeInterval: 0.5)
}
}
GCD的消息和信號(hào)量
class DispatchSourceAndSemaphoreTest {
public func DispatchSourceAndSemaphoreDemo() {
let source = DispatchSource.makeUserDataAddSource(queue: DispatchQueue.global())
source.setEventHandler {
print("Received Info!")
}
source.activate()
source.add(data: 1)
let semaphore = DispatchSemaphore.init(value: 0)
let queue = DispatchQueue.global()
queue.async {
Thread.sleep(forTimeInterval: 3)
semaphore.signal()
}
let start = Date()
semaphore.wait()
print("After \(Date().timeIntervalSince(start)) seconds, dispatchSemaphore test !")
}
}
DispatchSourceAndSemaphoreTest().DispatchSourceAndSemaphoreDemo()
Received Info!
After 3.0051910877227783 seconds, dispatchSemaphore test !
GCD執(zhí)行延時(shí)任務(wù)
class DispatchQueueTest {
public func DispatchQueueDemo() {
let queue = DispatchQueue.global()
let start = Date()
queue.asyncAfter(deadline: DispatchTime.now() + 3.0) {
print("After \(Date().timeIntervalSince(start)) seconds, block start to execute.")
}
}
}
DispatchQueueTest().DispatchQueueDemo()
并發(fā)編程三大問題
競態(tài)條件
指的是兩個(gè)或者兩個(gè)以上的線程對共享資源進(jìn)行讀寫操作,最終導(dǎo)致結(jié)果不確定乒疏。
class ConcurrentProgramProblem {
public func raceConditionTest() -> Int {
var num = 0
DispatchQueue.global().async {
for i in 0...50 {
num += i
}
}
for i in 0...50 {
num += i
}
return num
}
}
var ans = ""
for _ in 0...10 {
ans += " \(ConcurrentProgramProblem().raceConditionTest()) "
}
print(ans)
1803 2178 2310 2181 2310 2451 2517 2500 2550 2550 1710
Priority Inverstion(優(yōu)先倒置)
class ConcurrentProgramProblem {
public func priorityInverstionTest() {
let lowPriorityQueue = DispatchQueue(label: "Low Priority", qos: .utility, attributes: .concurrent, autoreleaseFrequency: .inherit, target: nil)
let highPriorityQueue = DispatchQueue(label: "High Priority", qos: .userInitiated, attributes: .concurrent, autoreleaseFrequency: .inherit, target: nil)
let semaphore = DispatchSemaphore.init(value: 1)
lowPriorityQueue.async {
// semaphore.wait()
for i in 0...10 {
print("\(Thread.current) i = \(i)")
}
// semaphore.signal()
}
highPriorityQueue.async {
// semaphore.wait()
for i in 11...20 {
print("\(Thread.current) i = \(i)")
}
// semaphore.signal()
}
}
}
ConcurrentProgramProblem().priorityInverstionTest()
明顯優(yōu)先級高的先執(zhí)行完额衙。
<NSThread: 0x6000013a7a40>{number = 4, name = (null)} i = 11
<NSThread: 0x6000013a7b00>{number = 3, name = (null)} i = 0
<NSThread: 0x6000013a7a40>{number = 4, name = (null)} i = 12
<NSThread: 0x6000013a7a40>{number = 4, name = (null)} i = 13
<NSThread: 0x6000013a7a40>{number = 4, name = (null)} i = 14
<NSThread: 0x6000013a7a40>{number = 4, name = (null)} i = 15
<NSThread: 0x6000013a7b00>{number = 3, name = (null)} i = 1
<NSThread: 0x6000013a7a40>{number = 4, name = (null)} i = 16
<NSThread: 0x6000013a7a40>{number = 4, name = (null)} i = 17
<NSThread: 0x6000013a7b00>{number = 3, name = (null)} i = 2
<NSThread: 0x6000013a7a40>{number = 4, name = (null)} i = 18
<NSThread: 0x6000013a7b00>{number = 3, name = (null)} i = 3
<NSThread: 0x6000013a7a40>{number = 4, name = (null)} i = 19
<NSThread: 0x6000013a7a40>{number = 4, name = (null)} i = 20
<NSThread: 0x6000013a7b00>{number = 3, name = (null)} i = 4
<NSThread: 0x6000013a7b00>{number = 3, name = (null)} i = 5
<NSThread: 0x6000013a7b00>{number = 3, name = (null)} i = 6
<NSThread: 0x6000013a7b00>{number = 3, name = (null)} i = 7
<NSThread: 0x6000013a7b00>{number = 3, name = (null)} i = 8
<NSThread: 0x6000013a7b00>{number = 3, name = (null)} i = 9
<NSThread: 0x6000013a7b00>{number = 3, name = (null)} i = 10
加入信號(hào)量:
<NSThread: 0x6000035d06c0>{number = 3, name = (null)} i = 0
<NSThread: 0x6000035d06c0>{number = 3, name = (null)} i = 1
<NSThread: 0x6000035d06c0>{number = 3, name = (null)} i = 2
<NSThread: 0x6000035d06c0>{number = 3, name = (null)} i = 3
<NSThread: 0x6000035d06c0>{number = 3, name = (null)} i = 4
<NSThread: 0x6000035d06c0>{number = 3, name = (null)} i = 5
<NSThread: 0x6000035d06c0>{number = 3, name = (null)} i = 6
<NSThread: 0x6000035d06c0>{number = 3, name = (null)} i = 7
<NSThread: 0x6000035d06c0>{number = 3, name = (null)} i = 8
<NSThread: 0x6000035d06c0>{number = 3, name = (null)} i = 9
<NSThread: 0x6000035d06c0>{number = 3, name = (null)} i = 10
<NSThread: 0x6000035d9ec0>{number = 6, name = (null)} i = 11
<NSThread: 0x6000035d9ec0>{number = 6, name = (null)} i = 12
<NSThread: 0x6000035d9ec0>{number = 6, name = (null)} i = 13
<NSThread: 0x6000035d9ec0>{number = 6, name = (null)} i = 14
<NSThread: 0x6000035d9ec0>{number = 6, name = (null)} i = 15
<NSThread: 0x6000035d9ec0>{number = 6, name = (null)} i = 16
<NSThread: 0x6000035d9ec0>{number = 6, name = (null)} i = 17
<NSThread: 0x6000035d9ec0>{number = 6, name = (null)} i = 18
<NSThread: 0x6000035d9ec0>{number = 6, name = (null)} i = 19
<NSThread: 0x6000035d9ec0>{number = 6, name = (null)} i = 20
Dead Lock(死鎖問題)
兩個(gè)或者多個(gè) 線程等待彼此執(zhí)行結(jié)束,以獲得某種資源怕吴,但是沒有一方會(huì)提前退出窍侧。
class ConcurrentProgramProblem {
public func deadLockTest() {
let operationA = BlockOperation()
let operationB = BlockOperation()
operationA.addDependency(operationB)
operationB.addDependency(operationA)
let serialQueue = DispatchQueue.init(label: "Serial Queue")
serialQueue.sync {
serialQueue.sync {
print("!!!!")
}
}
}
}