I was asked this question in interview for iOS developer role.
// Please design a read-write task queue where you can tag the reader task with label,
// where the the task with the same label should be executed sequentially, and the
// tasks with different labels could be executed concurrently. However, the writer
// would get the exclusive access where no concurrent task would happen at the
// same time with the writer task
// For example:
protocol ConcurrentQueueWithSerialization {
// Submits a labeled task.
// All tasks with the same label will be serialized.
// Tasks with different labels will run concurrently.
// Use this method to submit a "read" operation from a particular reader.
func async(with label: String, task: @escaping () -> Void)
// Submits a task that will run concurrently with all other tasks regardless of their labels.
func async(task: @escaping () -> Void)
// Submits a labeled and delayed task.
func asyncAfter(deadline: DispatchTime, with label: String, task: @escaping () -> Void)
// Submits an unlabeled and delayed task.
func asyncAfter(deadline: DispatchTime, task: @escaping () -> Void)
// Submits a barrier task. Only one barrier task is allowed to run at a time.
// Works as a critical section for the queue.
// Use this method to submit a writer task.
func asyncBarrier(task: @escaping () -> Void)
}
class MyDispatchQueue: ConcurrentQueueWithSerialization {
//TODO: write your implementation
}
Interviewer asked me to implement above protocol in MyDispatchQueue class. I tried but could not find solution. Please help me. Thanks in advance.
Previously I suggested using target queues, but even better, create a primary concurrent queue, and then create serial queues for the named queues, and then dispatch everything through that primary concurrent queue. Unlike the target queue approach, this will honor the scheduling of tasks dispatched to the named queues with those dispatched to the unnamed queue.
With that implementation, here's an example (an Instruments "Points of Interest" profile) of this where I added tasks for queues named "fred" and "ginger" and also one which was added to an unnamed queue, I then added a barrier task, and then added two more tasks to each of the aforementioned queues.
As you can see, it respects the serial nature of the named queues, the unnamed queue is concurrent, and all these queues are concurrent with respect to each other, but the barrier is a barrier across all the queues.
class MyDispatchQueue: ConcurrentQueueWithSerialization {
private var namedQueues = [String: DispatchQueue]()
private var queue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".target", attributes: .concurrent)
private let lock = NSLock()
private func queue(with label: String) -> DispatchQueue {
lock.lock()
defer { lock.unlock() }
if let queue = namedQueues[label] { return queue }
let queue = DispatchQueue(label: Bundle.main.bundleIdentifier! + "." + label)
namedQueues[label] = queue
return queue
}
func async(with label: String, task: @escaping () -> Void) {
queue.async {
self.queue(with: label).sync(execute: task)
}
}
func async(task: @escaping () -> Void) {
queue.async(execute: task)
}
func asyncAfter(deadline: DispatchTime, with label: String, task: @escaping () -> Void) {
queue.asyncAfter(deadline: deadline) {
self.queue(with: label).sync(execute: task)
}
}
func asyncAfter(deadline: DispatchTime, task: @escaping () -> Void) {
queue.asyncAfter(deadline: deadline, execute: task)
}
func asyncBarrier(task: @escaping () -> Void) {
queue.async(flags: .barrier, execute: task)
}
}
Note, I also synchronize access to the namedQueues
array, to ensure the thread-safety of this class.
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments