如何在iOS中将代码块分派到同一线程?

如何在iOS中将代码块分派到同一线程?,ios,asynchronous,sqlite,thread-safety,grand-central-dispatch,Ios,Asynchronous,Sqlite,Thread Safety,Grand Central Dispatch,问题的主要方面:是关于iOS的。我能否以某种方式分派代码块,使它们(a)在后台运行,(b)在同一线程上运行?我想在后台运行一些耗时的操作,但这些操作必须在同一个线程上运行,因为它们涉及资源,不能在线程之间共享 进一步的技术细节,如果需要:是关于为ApacheCordova实现一个sqlite插件,这是移动平台上HTML5应用程序的框架。这个插件应该是通过Cordova的插件API实现的。(这意味着,不可能将整个事务封装在单个块中,这会使一切变得更简单。) 以下是Cordova文档中的一些代码:

问题的主要方面:是关于iOS的。我能否以某种方式分派代码块,使它们(a)在后台运行,(b)在同一线程上运行?我想在后台运行一些耗时的操作,但这些操作必须在同一个线程上运行,因为它们涉及资源,不能在线程之间共享

进一步的技术细节,如果需要:是关于为ApacheCordova实现一个sqlite插件,这是移动平台上HTML5应用程序的框架。这个插件应该是通过Cordova的插件API实现的。(这意味着,不可能将整个事务封装在单个块中,这会使一切变得更简单。)

以下是Cordova文档中的一些代码:

- (void)myPluginMethod:(CDVInvokedUrlCommand*)command
{
    // Check command.arguments here.
    [self.commandDelegate runInBackground:^{
        NSString* payload = nil;
        // Some blocking logic...
        CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:payload];
        // The sendPluginResult method is thread-safe.
        [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
    }];
}

但据我所知,无法保证这些调度的代码块(请参见
runInBackground
)将在同一线程上运行。

您可以使用
NSOperationQueue
。您可以通过使用方法
-(void)setMaxConcurrentOperationCount:(NSInteger)count
使它只使用一个线程。将其设置为1

GCD不能保证两个块在同一线程上运行,即使它们属于同一队列(当然,主队列除外)。但是,如果您使用的是串行队列(
DISPATCH\u queue\u serial
),这不是一个问题,因为您知道不存在对数据的并发访问。

dispatch\u queue\u create
的手册页显示:

队列不绑定到任何特定的执行线程,提交到独立队列的块可以并发执行

我不知道有什么方法可以将队列绑定到特定的线程(毕竟,不需要关心线程是GCD的一个要点)之所以可以使用串行队列而不必担心实际线程,原因在于这样的承诺:

调度到串行队列的块执行的所有内存写入都保证对调度到同一队列的后续块可见

也就是说,似乎使用了记忆屏障

在处理线程问题时,您主要关心的是避免两个线程同时访问某些内容。如果您使用的是串行队列,则不会出现此问题。通常,哪个线程访问您的资源并不重要。例如,我们使用串行队列毫无问题地管理核心数据访问

编辑:

看起来你真的发现了一个罕见的情况,你需要在同一个线程上工作。您可以实现自己的工作线程:

  • 先决条件:
    • NSMutableArray(我们称之为
      blockQueue
    • 一个NSCondition(我们称之为
      queueCondition
  • 创建一个新的NSThread。
    • 线程的方法有一个无休止的循环,在这个循环中,它锁定条件,如果队列为空(并且“quit”bool为false),则等待条件,将块出列并执行它
  • 锁定条件并使块排队的方法
由于这种情况,线程将在没有工作要做时简单地休眠

因此,大致(未经测试,假设为ARC):


未经测试的Swift 5端口:

var workerThread: Thread?
var blockQueue = [() -> Void]()
let queueCondition = NSCondition()

func startWorkerThread() {
    workerThread = Thread() {
        let currentThread = Thread.current
        while true {
            self.queueCondition.lock()
            while self.blockQueue.isEmpty && !currentThread.isCancelled {
                self.queueCondition.wait()
            }

            if currentThread.isCancelled {
                self.queueCondition.unlock()
                return
            }

            let block = self.blockQueue.remove(at: 0)
            self.queueCondition.unlock()

            // Execute block outside the condition, since it's also a lock!
            // We want to give other threads the possibility to enqueue
            // a new block while we're executing a block.
            block()
        }
    }
    workerThread?.start()
}

func enqueue(_ block: @escaping () -> Void) {
    queueCondition.lock()
    blockQueue.append(block)
    queueCondition.signal()
    queueCondition.unlock()
}

func stopThread() {
    queueCondition.lock()
    workerThread?.cancel()
    queueCondition.signal()
    queueCondition.unlock()
}

创建一个串行调度队列,并将所有调用调度到该串行调度队列。所有调用都将在后台执行,但顺序是在同一线程上

如果要在主线程中执行选择器,可以使用

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait
如果您想让它在后台线程中执行

- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)object
如果您想在任何其他线程中执行,请使用GCD(
grandcentraldispatch


您可以将
NSOperationQueue
MaxConcurrentOperationCount
1
一起使用,也可以使用
NSThread
模型(而不是Grand Central Dispatch)手动执行。
使用后者,我建议您实现一个worker方法,该方法在线程中运行,并从从线程外部馈送的队列或池中提取工作包(或命令)。只需确保使用锁/互斥/同步。

从未尝试过这一点,但这可能会奏效。对每个操作使用
原子调度队列的单独属性

    @property (strong, atomic) dispatch_queue_t downloadQueue;
第一个操作的队列/线程1

    downloadQueue = dispatch_queue_create("operation1", NULL);
等等


由于
atomic
是线程安全的,因此其他线程不应访问
downloadQueue
。因此,它确保每个操作只有一个线程,其他线程将不会访问它。

在GCD中:不,这在当前的lib调度中是不可能的

块可以由dispatch lib在任何可用线程上执行,无论它们被分派到哪个队列

一个例外是主队列,它总是在主线程上执行其块

请向苹果公司提交一份功能请求,因为这似乎是合理的。但我担心这是不可行的,否则它就已经存在了;)

就这样

dispatch_asyn(dispatch_get_current_queue, ^ {
});

如果使用锁定,那么一次只能有一个线程访问API,这不一样吗?它不是一次只能执行一个而不是一个特定的线程吗?您的最后一个示例是在延迟后在主线程上执行的。另外,我认为您没有抓住OPs问题的重点。出于多种原因,
dispatch\u get\u current\u queue
将无法按“预期”工作,因此不推荐使用。无论如何,当此代码在辅助线程上运行,但尚未分派到分派队列时,您认为
dispatch\u get\u current\u queue()
会返回什么?非常感谢,这涵盖了大多数情况,并且已经帮了我很多忙。在特定情况下,我必须使用sqlite数据库。该数据库甚至可以在线程之间共享,但前提是任何事务都处于挂起状态。但这就是我的问题:交易将在第一个区块开始
    downloadQueue = dispatch_queue_create("operation1", NULL);
dispatch_asyn(dispatch_get_current_queue, ^ {
});