一间杂货铺~

02月 23

dispatch semaphore


I. 简述

含义

dispatch_semaphore_t/DispatchSemaphore

表示计数信号量。

函数

Objective-C

  1. dispatch_semaphore_create
  2. dispatch_semaphore_wait
  3. dispatch_semaphore_signal

Swift

  1. public init(value: Int)
  2. public func wait()public func wait(timeout: DispatchTime) -> DispatchTimeoutResultpublic func wait(wallTimeout: DispatchWallTime) -> DispatchTimeoutResult
  3. public func signal() -> Int

dispatch_semaphore_create

dispatch_semaphore_t dispatch_semaphore_create(long value)

创建计数信号量,value为其起始值。不能传入小于0的值。如果创建失败,则返回NULL

当两个线程需要协调特定事件的完成时,传入0
传入大于0的值,在管理有限资源池时是非常有用的,传入的值等于资源池的大小。

当不再需要信号量时,应当调用dispatch_release释放信号量(ARC下不能使用该函数)。

dispatch_semaphore_wait

long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout)

返回0,表示成功;否则,表示超时。

减小计数信号量。如果结果值小于0,这个函数在返回之前,会一直等待信号的发生。

dispatch_semaphore_signal

long dispatch_semaphore_signal(dispatch_semaphore_t dsema)

增大计数信号量。如果之前的值小于0,该函数会唤醒一个正在使用dispatch_semaphore_wait函数等待的线程。

II. 适用场景

在上面的dispatch_semaphore_create解释中,已经提到过。

  1. 对线程的运行进行协调
  2. 管理有限资源池

III. 实践

协调线程

也就是控制两个线程中特定事件的完成顺序。应用场景,比如经典的『生产-消费模型』。

在调用dispatch_semaphore_create函数时,必须传入0

以下面的示例为基础:

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        Thread.detachNewThreadSelector(#selector(_thread1(arg:)), toTarget: self, with: nil)
        Thread.detachNewThreadSelector(#selector(_thread2(arg:)), toTarget: self, with: nil)
    }

    @objc private func _thread1(arg: Any?) {
        for i in 0..<5 {
            print(#function + " \(i)")
        }
    }

    @objc private func _thread2(arg: Any?) {
        for i in 0..<5 {
            print(#function + " \(i)")
        }
    }

}

运行上面的代码,结果可能如下:

_thread2(arg:) 0
_thread1(arg:) 0
_thread2(arg:) 1
_thread1(arg:) 1
_thread2(arg:) 2
_thread1(arg:) 2
_thread2(arg:) 3
_thread1(arg:) 3
_thread2(arg:) 4
_thread1(arg:) 4

很明显,两个任务是交错运行的。

为了先执行完_thread2中的任务,再执行_thread1中的任务,使用信号量,就可以这样写:

class ViewController: UIViewController {

    let sema: DispatchSemaphore = DispatchSemaphore(value: 0)

    override func viewDidLoad() {
        super.viewDidLoad()

        Thread.detachNewThreadSelector(#selector(_thread1(arg:)), toTarget: self, with: nil)
        Thread.detachNewThreadSelector(#selector(_thread2(arg:)), toTarget: self, with: nil)
    }

    @objc private func _thread1(arg: Any?) {
        self.sema.wait()
        for i in 0..<5 {
            print(#function + " \(i)")
        }
    }

    @objc private func _thread2(arg: Any?) {
        for i in 0..<5 {
            print(#function + " \(i)")
        }
        self.sema.signal()
    }

}

运行结果就会是:

_thread2(arg:) 0
_thread2(arg:) 1
_thread2(arg:) 2
_thread2(arg:) 3
_thread2(arg:) 4
_thread1(arg:) 0
_thread1(arg:) 1
_thread1(arg:) 2
_thread1(arg:) 3
_thread1(arg:) 4

管理资源池

也就是协调有限资源的使用。

在创建信号量时,传入的值,要与可用的资源的数量一致。

拿园区小轿车停车为例,停车位属于有限的资源。

当停车位被占用后,其它小轿车是不能再占用该停车位的。

当园区中没有可用的停车位时,其它小轿车就得等待停车位上的小轿车离开后,才能去占用那个停车位。

class Car: NSObject {
    let identifier: String

    init(identifier: String) {
        self.identifier = identifier
    }

    override var description: String {
        get {
            return "{小车[\(self.identifier)]}"
        }
    }
}
/**
 停车位
 */
class ParkingSpace: NSObject {
    let identifier: String
    weak var car: Car? = nil

    var available: Bool {
        get {
            return self.car == nil
        }
    }

    init(identifier: String) {
        self.identifier = identifier
    }

    override var description: String {
        get {
            return "{停车位[\(self.identifier)]}"
        }
    }
}
class ViewController: UIViewController {

    let cars: [Car] = [
        Car.init(identifier: "京A 11111"),
        Car.init(identifier: "京B 22222"),
        Car.init(identifier: "京C 33333"),
        Car.init(identifier: "京D 44444"),
        Car.init(identifier: "京E 55555"),
    ]
    let parkingSpaces: [ParkingSpace] = [
        ParkingSpace.init(identifier: "001"),
        ParkingSpace.init(identifier: "002"),
        ParkingSpace.init(identifier: "003"),
    ]
    let parkingSpaceSemaphore: DispatchSemaphore
    let parkingSemaphore: DispatchSemaphore = DispatchSemaphore(value: 1)

    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
        self.parkingSpaceSemaphore = DispatchSemaphore(value: self.parkingSpaces.count)

        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
    }

    required init?(coder aDecoder: NSCoder) {
        self.parkingSpaceSemaphore = DispatchSemaphore(value: self.parkingSpaces.count)

        super.init(coder: aDecoder)
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        for car in self.cars {
            Thread.detachNewThreadSelector(#selector(_carParkingThread(arg:)), toTarget: self, with: car)
        }
    }

    @objc private func _carParkingThread(arg: Any?) {
        if let car = arg as? Car {
            print("\(car)要来停车啦")
            self.parkingSpaceSemaphore.wait()
            // 由于同时可能存在多个可用的停车位,如果不加锁,查找到的停车位可能是同一个,两辆车不能同时占用同一个停车位
            self.parkingSemaphore.wait()
            var availablePSs: [ParkingSpace] = []
            for ps in self.parkingSpaces {
                if ps.available {
                    availablePSs.append(ps)
                }
            }
            // 由于是通过parkingSpaceSemaphore来通知有可用的停车位的,因此availablePSs中总会有元素,并不需要检查availablePSs.count是否大于0
            let ps = availablePSs[Int(arc4random_uniform(UInt32(availablePSs.count)))]
            ps.car = car
            print("\(car)占用了\(ps)")
            self.parkingSemaphore.signal()
            let ti = 1 + arc4random_uniform(5)
            sleep(ti)
            ps.car = nil
            // 模拟为分钟
            print("\(ti)分钟后,\(car)离开了\(ps)")
            self.parkingSpaceSemaphore.signal()
        }
    }

}

运行结果可能如下:

{小车[京B 22222]}要来停车啦
{小车[京A 11111]}要来停车啦
{小车[京D 44444]}要来停车啦
{小车[京C 33333]}要来停车啦
{小车[京E 55555]}要来停车啦
{小车[京B 22222]}占用了{停车位[003]}
{小车[京A 11111]}占用了{停车位[002]}
{小车[京D 44444]}占用了{停车位[001]}
1分钟后,{小车[京D 44444]}离开了{停车位[001]}
{小车[京C 33333]}占用了{停车位[001]}
4分钟后,{小车[京A 11111]}离开了{停车位[002]}
{小车[京E 55555]}占用了{停车位[002]}
5分钟后,{小车[京B 22222]}离开了{停车位[003]}
5分钟后,{小车[京C 33333]}离开了{停车位[001]}
3分钟后,{小车[京E 55555]}离开了{停车位[002]}

标签:none

还不快抢沙发

添加新评论