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

时间:2018-03-01 05:39:05

标签: ios swift thread-safety grand-central-dispatch

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

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

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

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

struct Pixel {
  BYTE Blue;
  BYTE Green;
  BYTE Red;
  BYTE Alpha;
};

输出

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

2 个答案:

答案 0 :(得分:7)

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

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

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

let worker = Worker()
worker.enqueue { print("On background thread, yay") }

答案 1 :(得分:3)

你必须创建一个带有运行循环的自己的线程。 Apple为a custom run loop in Objective C举了一个例子。您可以在Swift中创建一个类似的线程类:

@app.route("/upload", methods=['POST'])
def upload():
    target = os.path.join(APP__ROOT, 'data/')
    print(target)
    if not os.path.isdir(target):
        os.mkdir(target)
    for file in request.files.getlist("file"):
        filename = file.filename
        print(filename)
        destination = "/".join([target, filename])
        print(destination)
        file.save(destination)
    return render_template("downloads.html")

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

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

注意 let thread = MyThread() thread.start() sleep(1) thread.runloop?.perform { print("task") } thread.runloop?.perform { print("task 2") } thread.runloop?.perform { print("task 3") } 不是很优雅但需要,因为线程需要一些时间才能启动。最好检查属性sleep是否已设置,并在必要时稍后执行该块。我的代码(特别是runloop)可能不适合竞争条件,而且仅用于演示。 ;-)