Search code examples
iosgrand-central-dispatchnsoperationqueuensthread

Any way to make dispatch_queue_t work in single thread?


Here is my code:

@interface MyObject ()
@property(nonatomic) dispatch_queue_t queue;
@end

@implementation MyObject {
    NSThread *_check;
}

- (id)init {
    self = [super init];
    if (self) {
        _queue = dispatch_queue_create("com.Thread.queue", NULL);
        dispatch_async(_queue, ^{
            _check = [NSThread currentThread]; //for ex. thread number = 3
            //some code here...
        });
    }

    return self;
}

- (void)someMethod:(MyObjClass *)obj {
    dispatch_async(_queue, ^{
        //need th
        if (_check != [NSThread currentThread]) { // it is sometimes number 3, but sometimes it changes
            NSLog(@"Thread changed.");
        }
        [obj doSmth]; //got crash if currentThread != _check         
    });
}

@end

I need to make sure that all MyObjClass's methods performs in the same thread. But this code change thread by it's own will, but sometimes it work in single thread. Any way I can force it to use same thread all the time?


Solution

  • In a word, no. Other than the main queue, GCD does not have any notion of thread affinity. If you really need thread affinity, GCD is not really the right tool. If you like the idiom, and want to kind of "adapt" something to your needs you could do something like this:

    @implementation AppDelegate
    {
        NSThread* thread;
    }
    
    void dispatch_thread_async(NSThread* thread, dispatch_block_t block)
    {
        if ([NSThread currentThread] == thread)
        {
            block();
        }
        else
        {
            block = [block copy];
            [(id)block performSelector: @selector(invoke) onThread: thread withObject: nil waitUntilDone: NO];
        }
    }
    
    void dispatch_thread_sync(NSThread* thread, dispatch_block_t block)
    {
        if ([NSThread currentThread] == thread)
        {
            block();
        }
        else
        {
            [(id)block performSelector: @selector(invoke) onThread: thread withObject: nil waitUntilDone: YES];
        }
    }
    
    - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
    {
        // Insert code here to initialize your application
        thread = [[NSThread alloc] initWithTarget: self selector:@selector(threadMain) object:nil];
        [thread start];
    
        dispatch_thread_async(thread, ^{
            NSLog(@"Async Thread: %@", [NSThread currentThread]);
        });
    
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            dispatch_thread_sync(thread, ^{
                NSLog(@"Sync Thread: %@", [NSThread currentThread]);
            });
        });
    }
    
    - (void)threadMain
    {
        // You need the NSPort here because a runloop with no sources or ports registered with it
        // will simply exit immediately instead of running forever.
        NSPort* keepAlive = [NSPort port];
        NSRunLoop* rl = [NSRunLoop currentRunLoop];
        [keepAlive scheduleInRunLoop: rl forMode: NSRunLoopCommonModes];
        [rl run];
    }
    
    @end