How to ensure to run some code on same background thread?

后端 未结 2 484
时光说笑
时光说笑 2020-12-10 07:13

I am using realm in my iOS Swift project. Search involve complex filters for a big data set. So I am fetching records on background thread.

But realm can be used onl

相关标签:
2条回答
  • 2020-12-10 07:23

    Here's a small worker class that can works in a similar fashion to async dispatching on a serial queue, with the guarantee that the thread stays the same for all work items.

    // 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()
        }
    }
    

    Just instantiate it and let it do the job:

    let worker = Worker()
    worker.enqueue { print("On background thread, yay") }
    
    0 讨论(0)
  • 2020-12-10 07:30

    You have to create your own thread with a run loop for that. Apple gives an example for a custom run loop in Objective C. You may create a thread class in Swift with that like:

    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
            }
        }
    }
    

    Now you can use it like this:

        let thread = MyThread()
    
        thread.start()
        sleep(1)
        thread.runloop?.perform {
            print("task")
        }
        thread.runloop?.perform {
            print("task 2")
        }
        thread.runloop?.perform {
            print("task 3")
        }
    

    Note: The sleep is not very elegant but needed, since the thread needs some time for its startup. It should be better to check if the property runloop is set, and perform the block later if necessary. My code (esp. runloop) is probably not safe for race conditions, and it's only for demonstration. ;-)

    0 讨论(0)
提交回复
热议问题