I created a sub-thread using NSThread in main thread
NSThread *newThread = [[NSThread alloc] initWithTarget:self selector:#selector(MyThread:) object:timer];
5 sec later,i used [newThread cancel] in main thread to stop the sub-thread,but it didnt work,
Method MyThread: in newThread still working
so,whats the correct answer to stop newThread,THX
actually [newThread isCancelled] is YES,but selector MyThread was still woking
The cancel method only informs the thread that it is cancelled (as you mentioned changes the isCancelled to YES. It's then the responsibility of the thread itself to check this and exit. For example, in your MyThread: method you could do this:
// At some checkpoint
if([[NSThread currentThread] isCancelled]) {
/* do some clean up here */
[NSThread exit];
}
You should do this check periodically, and exit from within the thread as shown; otherwise the cancel doesn't have any effect.
-(void)cancel
Discussion
The semantics of this method are the same as those used for the NSOperation object. This method sets state information in the receiver that is then reflected by the isCancelled method. Threads that support cancellation should periodically call the isCancelled method to determine if the thread has in fact been cancelled, and exit if it has been.
more information see NSThread API Reference
Related
I am detaching a thread to do some operation in the background, refer the code as below
currentThread = [[NSThread alloc]initWithTarget:contactServiceselector:#selector(requestForContactBackup:)object:msisdn];
[currentThread start];
This currentThread is the pointer declared in AppDelegate.
I have a button on my view, on tap of it, the execution of background thread should stop. Refer the below code:
-(void)cancelTheRunningTasks {
if(self.currentThread !=nil) {
[currentThread cancel];
NSLog(#"IsCancelled: %d",[currentThread isCancelled]); //here Yes returns
[self removeNetworkIndicatorInView:backUpViewController.view];
}
}
Problem with the below code is that the background thread is still remains in execution.
My question would be, having the thread reference, how to cancel/stop execution/kill the background thread from main thread?
please suggest me possible solution.
Thanks.
Your background thread needs to check to see if it has been cancelled, either through the isCancelled method...
if ([[NSThread currentThread] isCancelled]) {
// do cleanup here
[NSThread exit];
}
You can't kill the thread externally because there is no way to know what state the thread might be in and, thus, killing it would produce indeterminate behavior (imagine if the thread was holding a mutex down in the allocator when it was killed... ouch).
cancel
Changes the cancelled state of the receiver to indicate that it should exit.
exit
Terminates the current thread.
Check NSThread Class Reference
For more information about cancellation and operation objects, see NSOperation Class Reference.
Note: In OS X v10.6, the behavior of the cancel method varies depending on whether the operation is currently in an operation queue. For unqueued operations, this method marks the operation as finished immediately, generating the appropriate KVO notifications. For queued operations, it simply marks the operation as ready to execute and lets the queue call its start method, which subsequently exits and results in the clearing of the operation from the queue.
I resolved the Problem. Exactly what I was want to do that I want to stop or kill the working condition of some background thread from my main Thread or some other thread. As I read the Apple documentation and some posts I concluded that we can't kill one thread from other thread because they all threads shares common memory space and resources and its is not better to kill the thread by other thread (But one process can kill the other process because no common memory space shares between two processes).
Then I got info we cant exit/kill thread like that but still we can set the cancel property of the running thread from other thread. (In code where user requested to cancel the Tasks).
So here we can set cancel property. And inside our background task code which is under execution just check whether the cancel property is set or not. (we need to monitor after a chunk of execution of code). If cancel property is set/Yes then call [Thread exit] in that background thread code and release all the memory allocated by that thread to protect memory leaks (autorelease pool will not take care here for freeing the resources).
This is How i resolved the problem.
In simple --> just set the property of the particular task u want to cancel as cancel set. (method to set cancel will be call by the thread object reference).
if(self.currentThread != nil && [currentThread isExecuting])
{
[currentThread cancel];
}
And then monitoring in your code for cancel property. If property set then exit the thread.
if([appDelegate.currentThread isCancelled])
{
[NSThread exit];
}
If someone has better solution than this please refer. Otherwise It will also work fine.
How can I cancel performSelectorOnMainThread?
I have this code:
myClass = [[MyClass alloc] init];
[myClass performSelectorOnMainThread:#selector(setupPlayer) withObject:nil waitUntilDone:YES];
Per the documentation:
You cannot cancel messages queued using this method.
If you're careful about the thread on which you originally queued the message, you can cancel it by calling + (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget on that same thread. That previous answer was missing an important bit:
You cannot cancel messages queued using this method. If you want the
option of canceling a message on the current thread, you must use
either the performSelector:withObject:afterDelay: or
performSelector:withObject:afterDelay:inModes: method.
here is what i want:
create an object that 'lives' in its own thread, all the methods should be executed in that thread.
i.e:
// i'm in the main thread
MyClass *myObject = [ [MyClass alloc] init ]; // it creates its own thread
[myObject method1]; // should execute the method1 in myObject's thread
[myObject method2]; // should execute the method2 in myObject's thread
[myobject release]; // should deallocate everything that is used for myObject and remove myObject's thread
i have been reading about threads and runloops. I created a new thread on the init method, its entry point is the runloop method. The runloopMethod just set the most basic stuff needed for running a NSRunLoop and runs it.
aThread = [[NSThread alloc] initWithTarget:self selector:#selector(runloopMethod) object:nil];
[aThread start];
it worked fine, but when i call a method ( i.e: [myObject method1];) from the main thread it runs it on the main thread, how do i know it?, well, because method1 performs several operations that blocks the UI. What i have done is to redirect the call in this way:
// on MyClass.m
-(void) method1 {
if ([NSThread currentThread] != aThread) {
[self performSelector:#selector(method1) onThread:aThread withObject:nil waitUntilDone:YES];
}else {
// do my stuff
}
it's working, but this way limits me, also i have some questions for you:
i have realized that if i'm in X-thread and call a method of some object, it will be executed in X-thread. I think that the method call will be added (not sure if it's the word) to the X-thread's runloop. right?
Is there a way to set that: any call to my object's methods will be executed on the object's thread? (without doing all this stuff).
also, is it the correct way for what am i doing?
method1, method2, and so on are the sync version of my functions..so they will block the UI. that' why i assume having another thread is the way.
thanks for reading!.
btw. i'm not using GCD since i need to support iOS 3
The Objective C method dispatch runtime code has no mechanism (AFAIK) to determine implicitly whether to do a generic method call on a different thread than the current one, so you will have to implement your own explicit background call mechanism, as you did, using performSelector.
If you set waitUntilDone to YES on your call to your background thread from the main thread, you will still block the UI.
If you want your method1 to run in the background and not block the UI, set waitUntilDone to NO, and have to background thread inform the main thread about completion (or anything else) using performSelectorOnMainThread.
You might alternatively be able to use operation queues to send messages to your background thread's run loop.
I'm guessing you are trying to use threads to run background tasks in order to keep the UI responsive. That's good, but this would be a very difficult approach. Try this instead:
1) From the main thread, fire off a new thread:
[NSThread detachNewThreadSelector:#selector(methodThatTheThreadWillRun)
toTarget:nil
withObject:nil];
2) Write methodThatTheThreadShouldRun and do whatever you need to do in it. It will be executed in the thread you just created. When it finishes, have it call a threadIsFinished on the main thread:
- (void)methodThatTheThreadWillRun {
MyClass *myObject = [ [MyClass alloc] init ];
[myObject method1];
[myObject method2];
[myobject release];
[self performSelectorOnMainThread:#selector(threadIsFinished)];
}
3) Finally, write threadIsFinished:
- (void)threadIsFinished {
// do whatever you need to do here: stop a spinner, etc.
// this will be invoked by the background thread but will
// execute on the main thread
}
this is my first question here, so excuse me if I made any mistakes!
In my iPhone project I have a method running in a thread which takes a long time to execute (that's why it runs in a thread).
[NSThread detachNewThreadSelector:#selector(methodToBeCalledInAThread) toTarget:self withObject:nil];
// ...
-(void)methodToBeCalledInAThread {
MyClass *myClass = [[MyClass alloc] init];
[myClass setDelegate:self];
[myClass veryIntensiveComputing];
[myClass release];
}
My goal is to notifiy the ViewController calling this method of any progress going on in this method. This is why I set the ViewController as a delegate of the class.
Now in the expensive method I do the following:
if(self.delegate != nil) {
[self.delegate madeSomeProgress];
}
But unfortunately this does not work, because (I think) I'm in a background thread.
How do I achieve to notify the delegate of any changes with the method being executed asynchronously?
Try [self.delegate performSelectorOnMainThread: #selector(madeSomeProgress) withObject: nil waitUntilDone: YES];....
See the documentation for details.
This will synchronously perform the operation on the main thread. As long as nothing in the main thread tries to execute stuff in that secondary thread, this is mostly safe to do.
However if you don't want to block the computation until the main thread services the request, you can pass NO to not wait. If you do so, then you also have to worry about thread synchronization. The main thread may not immediately service the request and, when it does, your background thread may be in the process of mutating state.
Threads are hard.
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.