如何确保在同一个后台线程上运行一些代码?

Ati*_*tif 7 thread-safety realm grand-central-dispatch ios swift

我在我的iOS Swift项目中使用领域.搜索涉及大数据集的复杂过滤器.所以我在后台线程上获取记录.

但是,realm只能在创建Realm的同一个线程中使用.我正在保存在后台线程上搜索Realm后得到的结果的引用.该对象只能从同一个后台线程访问

如何确保在不同时间将代码分发到同一个线程?

我按照建议解决了这个问题,但是没有用

let realmQueue = DispatchQueue(label: "realm")
    var orginalThread:Thread?

    override func viewDidLoad() {
        super.viewDidLoad()

        realmQueue.async {
            self.orginalThread = Thread.current
        }

        let deadlineTime = DispatchTime.now() + .seconds(2)
        DispatchQueue.main.asyncAfter(deadline: deadlineTime) {
            self.realmQueue.async {
                print("realm queue after some time")

                if self.orginalThread == Thread.current {
                    print("same thread")
                }else {
                    print("other thread")
                }
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

输出是

realm queue after some time

other thread
Run Code Online (Sandbox Code Playgroud)

Cri*_*tik 8

这是一个小型工作者类,它可以以与串行队列上的异步调度类似的方式工作,并保证线程对所有工作项保持相同.

// Performs submitted work items on a dedicated thread
class Worker {

    // the worker thread
    private var thread: Thread?

    // used to put the worker thread in the sleep mode, so in won't consume
    // CPU while the queue is empty
    private let semaphore = DispatchSemaphore(value: 0)

    // using a lock to avoid race conditions if the worker and the enqueuer threads
    // try to update the queue at the same time
    private let lock = NSRecursiveLock()

    // and finally, the glorious queue, where all submitted blocks end up, and from
    // where the worker thread consumes them
    private var queue = [() -> Void]()

    // enqueues the given block; the worker thread will execute it as soon as possible
    public func enqueue(_ block: @escaping () -> Void) {
        // add the block to the queue, in a thread safe manner
        locked { queue.append(block) }

        // signal the semaphore, this will wake up the sleeping beauty
        semaphore.signal()

        // if this is the first time we enqueue a block, detach the thread
        // this makes the class lazy - it doesn't dispatch a new thread until the first
        // work item arrives
        if thread == nil {
            thread = Thread(block: work)
            thread?.start()
        }
    }

    // the method that gets passed to the thread
    private func work() {
        // just an infinite sequence of sleeps while the queue is empty
        // and block executions if the queue has items
        while true {
            // let's sleep until we get signalled that items are available
            semaphore.wait()

            // extract the first block in a thread safe manner, execute it
            // if we get here we know for sure that the queue has at least one element
            // as the semaphore gets signalled only when an item arrives
            let block = locked { queue.removeFirst() }
            block()
        }
    }

    // synchronously executes the given block in a thread-safe manner
    // returns the same value as the block
    private func locked<T>(do block: () -> T) -> T {
        lock.lock(); defer { lock.unlock() }
        return block()
    }
}
Run Code Online (Sandbox Code Playgroud)

只是实例化它并让它完成工作:

let worker = Worker()
worker.enqueue { print("On background thread, yay") }
Run Code Online (Sandbox Code Playgroud)


cle*_*ens 2

您必须为此创建自己的带有运行循环的线程。Apple 给出了Objective C 中自定义运行循环的示例。你可以在 Swift 中创建一个线程类,如下所示:

class MyThread: Thread {
    public var runloop: RunLoop?
    public var done = false

    override func main() {
        runloop = RunLoop.current
        done = false
        repeat {
            let result = CFRunLoopRunInMode(.defaultMode, 10, true)
            if result == .stopped  {
                done = true
            }
        }
        while !done
    }

    func stop() {
        if let rl = runloop?.getCFRunLoop() {
            CFRunLoopStop(rl)
            runloop = nil
            done = true
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在你可以像这样使用它:

    let thread = MyThread()

    thread.start()
    sleep(1)
    thread.runloop?.perform {
        print("task")
    }
    thread.runloop?.perform {
        print("task 2")
    }
    thread.runloop?.perform {
        print("task 3")
    }
Run Code Online (Sandbox Code Playgroud)

注意:这sleep不是很优雅,但是是必需的,因为线程需要一些时间来启动。最好检查该属性是否runloop已设置,并在必要时稍后执行该块。我的代码(尤其是runloop)对于竞争条件可能不安全,并且仅用于演示。;-)