Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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 only from same thread on which Realm was created. I am saving a reference of results which I got after searching Realm on background thread. This object can only be access from same back thread

How can I ensure to dispatch code at different time to the same thread?

I tried below as suggested to solve the issue, but it didn't worked

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

Output is

realm queue after some time

other thread
like image 431
Atif Avatar asked Mar 01 '18 05:03

Atif


People also ask

How do I run a background thread?

To specify the thread on which to run the action, construct the Handler using a Looper for the thread. A Looper is an object that runs the message loop for an associated thread. Once you've created a Handler , you can then use the post(Runnable) method to run a block of code in the corresponding thread.

What is the best way for performing tasks on a background thread in Kotlin?

So in your Kotlin programs, performing heavy operations like network calls, interacting with Database/Device file system, etc, etc. You need to execute those tasks in the background thread. That's means you should consider using the Asynchronous approach.

How do I run an app on a background thread?

You have to explicitly say when you want to run your app code on a background thread. In C# and Visual Basic you can accomplish this by passing code to Task.Run. Remember that UI elements may only be accessed from the UI thread.

Will the work I schedule run on a background thread?

But this doesn't guarantee that the work you schedule will run on a background thread. Many of the Universal Windows Platform (UWP) APIs schedule work in the background thread for you, but if you call your app code by using only await or a delegate, you run that delegate or method on the UI thread.

How do I update the UI from a background thread?

You can't update the UI from a background thread but you can post a message to it with CoreDispatcher.RunAsync to cause code to be run there. Note The one exception is that there's a separate render thread that can apply UI changes that won't affect how input is handled or the basic layout.

What kind of work can be performed on a background thread?

An example of work that can be performed on a background thread is the calculating of computer AI in a game. The code that calculates the computer's next move can take a lot of time to execute.


2 Answers

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") }
like image 82
Cristik Avatar answered Oct 17 '22 16:10

Cristik


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. ;-)

like image 44
clemens Avatar answered Oct 17 '22 15:10

clemens