GCD 不保证两个块在同一个线程上运行,即使它们属于同一个队列(当然主队列除外)。然而,如果您使用串行队列(DISPATCH_QUEUE_SERIAL
)这不是问题,因为您知道没有对数据的并发访问。
的手册页dispatch_queue_create
says:
队列不绑定到任何特定的执行线程,提交到独立队列的块可以同时执行。
我不知道有什么方法可以将队列绑定到特定线程(毕竟,不需要关心线程是 GCD 的一个要点)。您可以使用串行队列而不用担心实际线程的原因是这样的:
调度到串行队列的块执行的所有内存写入都保证对调度到同一队列的后续块可见。
也就是说,似乎使用了内存屏障。
在处理线程问题时,您主要关心的通常是避免两个线程同时访问某些内容。如果您使用串行队列,则不会遇到此问题。通常并不重要which线程正在访问您的资源。例如,我们使用串行队列来毫无问题地管理核心数据访问。
Edit:
看来您确实发现了一种罕见的情况,您需要在同一线程上工作。您可以实现自己的工作线程:
- Prerequisites:
- 一个 NSMutableArray (我们称之为
blockQueue
).
- 一个 NSCondition (我们称之为
queueCondition
).
- Create a new NSThread.
- 线程的方法有一个无限循环,其中锁定条件,如果队列为空(并且“quit”布尔为假)则等待它,使块出列并执行它。
- 锁定条件并将块排入队列的方法。
由于这种情况,线程将在没有工作可做时简单地休眠。
所以,粗略地(未经测试,假设 ARC):
- (void)startWorkerThread
{
workerThread = [[NSThread alloc]
initWithTarget:self
selector:@selector(threadMain)
object:nil
];
[workerThread start];
}
- (void)threadMain
{
void (^block)();
NSThread *currentThread;
currentThread = [NSThread currentThread];
while (1) {
[queueCondition lock];
{
while ([blockQueue count] == 0 && ![currentThread isCancelled]) {
[queueCondition wait];
}
if ([currentThread isCancelled]) {
[queueCondition unlock];
return;
}
block = [blockQueue objectAtIndex:0];
[blockQueue removeObjectAtIndex:0];
}
[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();
}
}
- (void)enqueue:(void(^)())block
{
[queueCondition lock];
{
// Copy the block! IIRC you'll get strange things or
// even crashes if you don't.
[blockQueue addObject:[block copy]];
[queueCondition signal];
}
[queueCondition unlock];
}
- (void)stopThread
{
[queueCondition lock];
{
[workerThread cancel];
[queueCondition signal];
}
[queueCondition unlock];
}
未经测试的 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()
}