简体   繁体   中英

Main thread hangs indefinitely while waiting for NSOperationQueue operations to cancel [Only on Device!]

I have an NSOperationQueue on my main thread running a set of NSOperations (max concurrent set to 1) that I want to be able to cancel at any time. When I press a button I tell the queue to cancel all operations and wait until finished. This should hang the main thread until the operation queue is empty, however it is hanging my main thread indefinitely.

Here's the code I use to stop it:

...
[myQueue cancelAllOperations];
[myQueue waitUntilAllOperationsAreFinished];
return YES; // This line never gets called

Note: I need to use waitUntilAllOperationsAreFinished as further processes require that the queue be empty.

The strange thing is this is only occurring on the device. When running in the simulator it works as expected.

I have watched breakpoints and I can follow the currently running operation until it finishes. It detects [self isCancelled], stops what it's doing and zips through to the end of the main method. I can see that nothing in the operation is causing it to hang, and by cancelling all operations, none of the other operations should start, and the queue should finish. I have checked by adding breakpoints and none of the other operations start.

Why is this happening?

In any of your operations (or in any other thread), are you using -performSelectorOnMainThread:withObject:waitUntilDone: ? -waitUntilAllOperationsAreFinished will block whatever thread it's called on until all operations are complete. Odds are, if you're calling this as the application is terminating, that thread would be the main thread. If the main thread is blocked, and one of the operations uses -performSelectorOnMainThread:withObject:waitUntilDone: , your application will freeze because the operation will never complete.

I've had this exact thing happen to me before. Unfortunately, it's pretty difficult to work around.

You should never, ever block the main thread. It handles all your UI updates for one thing, for another as you have noted you managed to create a deadlock.

Instead, try creating a method like:

- (void) notifyOnFinish
{
   [myQueue waitUntilAllOperationsAreFinished];
   [self performSelectorOnMainThread:(queueEmpty) withObject:nil waitUntilDone:NO];
}

Then where you have your code now, call:

[myQueue cancelAllOperations];
[self performSelectorInBackground:@selector(notifyOnFinish) withObject:nil];

And in a queueEmpty method you just do whatever you want to do when the queue is emptied.

Basically, just create a background thread to block instead of the main thread.

Perhaps the waitUntilAllOperationsArefFinished is causing the block... Maybe the operations all cancel and finish before the call to waitUntilAllOperationsArefFinished and then the queue is sat hanging, waiting for operations that are already finished to finish...?

I don't know this for a fact, but maybe try not calling waitUntilAllOperationsArefFinished .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM