我们如何在swift中使用协议实现并发线程?

时间:2021-03-17 07:33:21

I was asked this question in interview for iOS developer role.

我在面试中被问到iOS开发者角色这个问题。

// 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.

采访者让我在MyDispatchQueue类中实现上述协议。我试过但找不到解决方案。请帮我。提前致谢。

1 个解决方案

#1


6  

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.

有了这个实现,这里有一个例子(一个乐器“兴趣点”的配置文件),我在其中添加了名为“fred”和“ginger”的队列的任务,还有一个添加到未命名队列的任务,然后我添加了一个屏障任务,然后为每个上述队列添加两个任务。

我们如何在swift中使用协议实现并发线程?

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.

注意,我还同步访问namedQueues数组,以确保此类的线程安全。

#1


6  

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.

有了这个实现,这里有一个例子(一个乐器“兴趣点”的配置文件),我在其中添加了名为“fred”和“ginger”的队列的任务,还有一个添加到未命名队列的任务,然后我添加了一个屏障任务,然后为每个上述队列添加两个任务。

我们如何在swift中使用协议实现并发线程?

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.

注意,我还同步访问namedQueues数组,以确保此类的线程安全。

相关文章