5

我有一个简单的场景,其中 aNSTimer被安排在后台线程上以每隔一段时间通过套接字发送数据包。然而,我的主线程负责销毁计时器和套接字。

破坏看起来像这样:

if (self.connected) {
    [self.pingTimer invalidate];

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

    self.connected = NO;
}

如何在 Objective-C 中确保一旦输入这部分代码,调度程序就不允许挂起这个线程,直到代码部分结束?换句话说,我想让这个代码部分原子化。

我的理解是该@synchronized指令是一个高级信号量/互斥体,因此为了确保该代码块的原子性,我需要将代码块本身以及所有引用self.connected,self.pingTimer和指令的代码括self.socket起来。@synchronized我这样想对吗?

4

2 回答 2

3

@synchronized是一种方式。另一种是使用NSLockor NSRecursiveLock。您甚至可以使用普通的 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;
    }
}
于 2012-09-21T08:46:10.440 回答
2

据我所知,没有办法阻止操作系统抢占您的线程并在其位置运行另一个。你能做的最好的就是同步访问。但是在阅读您的代码时,我推断出以下内容:

  • connected 是一个内部状态变量。
  • 您想避免在断开连接时安排计时器。
  • 您希望避免在安排计时器时断开连接。

我认为最好尽可能避免同步。幸运的是,您使用的是 NSTimer,这将使事情变得微不足道。

NSTimer 不在后台线程上运行,而是在 NSRunLoop 上运行,并且可能在主运行循环上运行,就像你的dealloc. 计时器是运行循环的事件源。当他们触发时,他们将一个事件排入队列,然后在处理它时调用您的代码。因为运行循环一次处理一个事件,这意味着您计划的计时器中的代码和您的 dealloc 中的代码在另一个可以运行之前得到完全执行。这也意味着您不需要任何特殊的同步机制。

- (void) dealloc
{
   // ...
   if (_connected) {
       [_pingTimer invalidate];
       if (_socket != -1)
           close(_socket);
   }
   // ...
   [super dealloc];
}

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

于 2012-09-22T11:41:14.237 回答