Any way to make dispatch_queue_t work in single th

2019-04-09 16:44发布

问题:

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?

回答1:

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


回答2:

In case you have multiple instances of your class, you are overwriting your queue with each new init.

A singleton or a static could be used to resolve the issue.