在Objective-C中,如何标记一个关键区域?

5
我有一个简单的场景,其中一个 NSTimer 被安排在后台线程上定期通过套接字发送数据包。然而,我的主线程负责销毁计时器和套接字。
销毁过程如下:
if (self.connected) {
    [self.pingTimer invalidate];

    if (self.socket != -1) {
        close(self.socket);
        self.socket = -1;
    }

    self.connected = NO;
}

我该如何确保在Objective-C中,一旦进入代码段,调度程序不允许挂起此线程,直到代码段结束?换句话说,我想使这个代码段是原子的。
据我了解,@synchronized指令是一个高级信号量/互斥锁,因此要确保该代码块的原子性,我需要将代码块本身以及所有引用self.connectedself.pingTimerself.socket的代码都放入@synchronized指令中。我的想法正确吗?

@synchronized是一个信号量,也就是说,如果您有两个线程在同一对象上进行同步,那么一次只能有一个线程拥有信号量。因此,它不仅保护了它所包围的区域,还保护了从该区域调用的任何内容。据我所知,在Objective-C级别上没有实现“临界区”以防止调度的方法。 - Hot Licks
2个回答

3

@synchronized 是其中一种方式。另一种方式是使用 NSLockNSRecursiveLock。你甚至可以使用普通的 POSIX pthread 锁原语。请参阅多线程指南

@interface Stuff 
{
    NSLock* lock;
    int cell;
}

- (int) cellValue;
- (void) someOperation;

@end

@implementation Stuff

- (id) init  
{
    if (self = [super init]) 
    {
        lock = [[NSLock alloc] init];
        cell = 0;
    }
    return self;
}

- (int) cellValue 
{
    [lock lock];
    @try
    {
        return cell;
    }
    @finally 
    {
        [lock unlock];
    }
}

- (void) someOperation 
{
    [lock lock];
    @try
    {
        // Do something involving access to stuff protected by
        // the lock (only cell here, but could be more, of course)
    }
    @finally 
    {
        [lock unlock];
    }
}

@end

使用@synchronized,上面的代码会更简单:

- (int) cellValue 
{
    @synchronized (self)
    {
        return cell;
    }
}

2
据我所知,没有办法防止操作系统抢占您的线程并运行另一个线程。最好的方法是同步访问。但是,在阅读您的代码时,我推断出以下内容:
- connected 是内部状态变量。 - 您想在断开连接时避免定时器被调度。 - 您想在定时器被调度时避免断开连接。
我认为尽可能避免同步是最好的。幸运的是,您正在使用 NSTimer,这将使事情变得非常简单。
NSTimer 不会在后台线程上运行,而是在 NSRunLoop 上运行,并且可能与您的 dealloc 一样在主 run loop 上运行。计时器是运行循环的事件源。当它们触发时,它们将排队一个事件,该事件在处理时调用您的代码。因为运行循环一次只处理一个事件,这意味着您安排的定时器和 dealloc 中的代码都将完全执行,然后才能运行另一个代码。这也意味着您不需要任何特殊的同步机制。
- (void) dealloc
{
   // ...
   if (_connected) {
       [_pingTimer invalidate];
       if (_socket != -1)
           close(_socket);
   }
   // ...
   [super dealloc];
}

注意:我在可以的情况下更喜欢直接使用状态变量,除非设置器有其他副作用。在dealloc中设置状态变量也毫无意义,因为一旦方法调用完成,对象就会消失。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接