In Objective-C, how do you mark a critical region?

I have a simple scenario when an NSTimer scheduled in the background thread, which so often sends a packet through a socket. However, my main thread is responsible for destroying the timer and socket.

Destruction is as follows:

 if (self.connected) { [self.pingTimer invalidate]; if (self.socket != -1) { close(self.socket); self.socket = -1; } self.connected = NO; } 

How can I ensure in Objective-C that after entering this section of code, the scheduler is not allowed to pause this thread until the code section ends? In other words, I want to make this section of code atomic.

I understand that the @synchronized directive is a high-level semaphore / mutex, so to ensure the atomicity of this code block, I will need to enclose the code block itself, as well as all the code that refers to either self.connected , self.pingTimer and self.socket in @synchronized directives. Am I correct in this?

+4
source share
2 answers

As far as I know, there is no way to prevent the OS from being squeezed out of the thread and restarting in its place. The best you can do is synchronize access. But while reading your code, I do the following:

  • - internal state variable.
  • You want the timer not to be scheduled during a shutdown.
  • You want to avoid a trip when scheduling a timer.

I think it's best to avoid synchronization whenever possible. Fortunately, you are using NSTimer, which will make things trivial.

NSTimer does NOT start in the background thread, but on NSRunLoop and probably in the main run loop, like your dealloc . Timers are event sources for the trigger cycle. When they fire, they trigger an event that, in turn, triggers your code when it is processed. Since the run loops process one event at a time, this means that the code in your scheduled timer and the code in your dealloc are fully executed before the other can work. It also means that you do not need any special synchronization mechanism.

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

Note. I prefer to use state variables directly when I can, unless setters have other side effects. It also makes no sense to set state variables in dealloc simply because the object will disappear after the method call completes.

+2
source

@synchronized is one way. Another option is to use NSLock or NSRecursiveLock . You can even use simple POSIX primitives to commit pthread. See the Multithreading Guide .

 @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 

Using @synchronized , the code above would look simpler:

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

Source: https://habr.com/ru/post/1435460/


All Articles