Re: NSThread question - DO to make it sing
Re: NSThread question - DO to make it sing
- Subject: Re: NSThread question - DO to make it sing
- From: Miguel Morales <email@hidden>
- Date: Fri, 20 Jul 2001 15:45:29 -0700
Candide,
Sorry for the long wait before replying (drowning here).
I'm not very familiar with java, but are you sure that the snippet below
runs in parallel? It may depend on whether the function returns a value
(what would the return get assigned to, and how would the main thread
now the value had been updated or not if a value is returned?). The real
answer may have to do with the libraries you are using in java, and
whether they are thread safe. If you had a thread safe window
displaying function, then it could just be told to update by multiple
threads as your original code suggested. This may be possible in Cocoa,
but I write command line tools and know nothing about the graphics
system. I do know that because of worries with return values, the Obj-C
runtime looks at the return values to determine if it can be safely run
asynchronously (the oneway keyword tells the runtime that yes it can).
For the run loop, yes this is your application. I write command line
tools, and so I have to explicitly enter the run loop. This can be
useful however for you as well, because a new thread does not
automatically enter it's run loop. You can either have the thread exit,
as in your example, or you can have it register it's name and enter a
run loop. In this case the thread sticks around, and other threads can
make proxies to this thread, and it will wait around for jobs to do. I
use this in my project where I have ~10 threads, each specialized to a
particular task. I give one thread data, and it processes it and passes
it to the next thread, etc. down the line. Essentially instead of just
having a temporary thread for processing, my threads act more like
independent programs that are communicating back and forth, and running
in the same memory space.
As to the errors, I think someone else pointed out the need for the ":"
in the method name (I stumbled over this one too).
Good luck with all this,
-Miguel
On Wednesday, July 11, 2001, at 08:47 AM, Candide Kemmler wrote:
>
Le mardi 10 juillet 2001, ` 06:54, Miguel Morales a icrit :
>
>
>
> However, this would spawn a thread, but the main thread would wait for
>
> the secondary thread to finish before continuing, and not give you any
>
> of the functionality you want of the main thread plugging along. This
>
> is where DO comes in. I will assume that there is a main object that
>
> is running in a run loop, and that when the button gets pushed the
>
> method -(void)button:(NSData *)data is called, and that there is
>
> another method in the object that draws to the screen (also in the
>
> main object) called -(oneway void)display:(bycopy NSData *)data.
>
>
Great ! I've read the chapter on DO in "Object-Oriented Programming and
>
the Objective-C Language" and I'm enthusiast about learning more on DO
>
in OC. For a java-fan like me, it's like doing RMI and multi-threading
>
at once ! However I find it a little odd to be forced to do rmi (or
>
rpc, or whatever-you-name-it) just to do multi-threading.
>
>
You said that the code snippet you give "would spawn a thread, but the
>
main thread would wait for the secondary thread to finish before
>
continuing" !!!!! That's the whole point of doing threads !
>
>
Consider the following bit of java code:
>
>
public void actionPerformed ( ActionEvent evt ) {
>
if ( evt.getSource () == myButton ) {
>
( new Thread () {
>
public void run () {
>
renderMyImage ();
>
}
>
} ).start ();
>
}
>
}
>
>
Isn't there a way as simple as this do achieve the same effect in
>
Objective-C/Cocoa ?
>
>
Anyway I've tried to go your way, but I wasn't lucky:
>
>
I wrote a MapRenderer class like this:
>
>
@implementation MapRenderer
>
>
- (oneway void) renderImage: (bycopy NSData *) data
>
{
>
id mainProxy;
>
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init ];
>
NSSize aSize;
>
NSImage *cachedImage;
>
>
aSize = NSMakeSize (450, 345 );
>
>
mainProxy = [ NSConnection
>
rootProxyForConnectionWithRegisteredName:@"mainThread" host:@"" ];
>
cachedImage = [[NSImage alloc]
>
initWithSize:NSMakeSize(aSize.width, aSize.height)];
>
[ cachedImage lockFocus ];
>
[ [ NSColor blueColor ] set ];
>
[[ NSBezierPath bezierPathWithRect:NSMakeRect ( 0, 0, aSize.width,
>
aSize.height )] fill ];
>
[ cachedImage unlockFocus ];
>
>
[ mainProxy setImage: cachedImage ];
>
>
[ pool release ];
>
}
>
>
in my MapController class, I have a masterConnection defined in init
>
like you said and also a mapRenderer allocated and initialized.
>
Naturally I also have a setImage: (NSImage *) anImage method
>
>
When my button's triggered, I do:
>
>
[ NSThread detachNewThreadSelector:@selector(renderImage)
>
toTarget:mapRenderer withObject:data ];
>
>
The result is a catastrophy of errors and exceptions:
>
>
_NSAutoreleaseNoPool ():...
>
-[MapRenderer renderImage]: selector not recognized
>
>
just leaking, just leaking,...
>
selector not recognized..., InvalidArgumentException...
>
>
What's wrong ???
>
>
And a big thank you for your long explanations ! :-)
>
>
>
Regards,
>
>
Candide
>
>
PS: I don't get the following: "I will assume that there is a main
>
object that is running in a run loop". Isn't that my application ?
>
>
> in the main object (main thread)
>
> ---------------
>
> //In some initialization routine (-init?)
>
> masterConnection = [NSConnection defaultConnection]; //Gets the default
>
> connection for this thread
>
> [masterConnection setRootObject:self];
>
> [masterConnection registerName:@"mainThread"]; //registers itself
>
> on the network so it can take input
>
> .........
>
>
>
> -(void)button:(NSData *)data
>
> {
>
> [NSThread detachNewThreadSelector:@selector(setData)
>
> toTarget: myView withObject: data];
>
> }
>
>
>
> in the myView object (will be in a new thread most of the time)
>
> ---------------
>
> -(oneway void) setData:(bycopy NSData *) data
>
> {
>
> id mainProxy; //A proxy object for the main object in the
>
> main thread (how we call back when done)
>
>
>
> NSAutoreleasePool *pool = [[NSAutoreleasePool alloc ] init];
>
>
>
> mainProxy = [NSConnection
>
> rootProxyForConnectionWithRegisteredName:@"mainThread" host:@""];
>
> //picks up the network connection mainThread, and makes
>
> a proxy object so we can call home
>
>
>
> myData = data; //Already a copy from the bycopy keyword
>
> [self doImage]; //do processing, but DO NOT write to the screen
>
>
>
> [mainProxy display: myData];
>
> //Calls back to the main thread, giving it the updated
>
> information, and telling it to draw it to
>
> //the screen (so only the main thread draws to the screen)
>
>
>
> [pool release];
>
> //Clean up afterwards, thread should automatically die
>
> because we have no run loop for this thread
>
> }
>
>
>
> Note the oneway and bycopy keywords. oneway tells the DO system that
>
> setData can be run independently of the main thread, otherwise the
>
> main thread will wait for the sub thread to do it's processing (not
>
> what you want). bycopy tells DO to send a copy of the data object,
>
> not a proxy. This can greatly reduce the overhead of sending messages
>
> in some situations.
>
>
>
> Now the caveats: I just typed this in, and DO can be confusing, so
>
> this is probably not bug free (some of the DO mavens out there can
>
> point out my mistakes :-) The DO is hard to learn at first. Look at
>
> Object-Orient Programming and the Objective-C Language, and some of
>
> the DO examples. But once you get DO it is very cool. With a couple
>
> of very minor changes the above code can be changed so that the
>
> secondary thread persists and can process data whenever you want (just
>
> have the thread vend a connection and send the thread into a run
>
> loop), or the data can be processed by a process on another machine
>
> (some people prefer to set up ports instead of registering a name when
>
> just doing thread to thread communication, but I do distributed
>
> processing, and like the elegance of the same interface whether the
>
> communication is local or remote) So DO is definitely worth the time,
>
> and have fun.
>
>
>
> Hope this helps,
>
>
>
> -Miguel F. Morales
>
>
>
>
>
>
>
>
>
>> Candide wrote:
>
>>
>
>>
>
>> I still have an error (signal 10 (SIGBUS)). I'll explain exactly
>
>> what I
>
>> do:
>
>>
>
>> I have a -(void)doImage; method in a custom NSView object. This is the
>
>> method I'm calling in the new Thread, and it goes like this:
>
>>
>
>> -(void)doImage
>
>> {
>
>> NSAutoreleasePool *pool = [[NSAutoreleasePool alloc ] init];
>
>> [ self display ];
>
>> }
>
>>
>
>> the method in which I call it goes like this:
>
>>
>
>> -(void) setData:(NSData *) data
>
>> {
>
>> myData = [[NSData alloc] initWithData:data];
>
>> cachedImage = nil;
>
>> [NSThread detachNewThreadSelector:@selector(doImage) toTarget:self
>
>> withObject:nil];
>
>> }
>
>>
>
>> Still obscure to me what causes the crash...
>
>>
>
>>> Le mardi 10 juillet 2001, ` 11:59, Stefan Jung a icrit :
>
>>>
>
>>> Am Dienstag, 10. Juli 2001 um 10:47 schrieb Candide Kemmler:
>
>>>
>
>>>> Hi,
>
>>>>
>
>>>> I have a button (several of them actually) which triggers a long
>
>>>> rendering. My application not being multithreaded, the button stays
>
>>>> blue for a long while, until the rendering's finished. I tried to
>
>>>> do:
>
>>>>
>
>>>> [ NSThread detachNewThreadSelector:@selector(display) toTarget:self
>
>>>> withObject:nil ];
>
>>>>
>
>>>> But then I get
>
>>>>
>
>>>> Jul 10 10:46:50 TT[979] *** _NSAutoreleaseNoPool(): Object 0x145d390
>
>>>> of class NSBezierPath autoreleased with no pool in place - just
>
>>>> leaking
>
>>>> Jul 10 10:46:50 TT[979] *** _NSAutoreleaseNoPool(): Object 0x145d3f0
>
>>>> of class NSBezierPath autoreleased with no pool in place - just
>
>>>> leaking
>
>>>> Jul 10 10:46:50 TT[979] *** _NSAutoreleaseNoPool(): Object 0x145d300
>
>>>> of class NSCalibratedRGBColor autoreleased with no pool in place -
>
>>>> just leaking
>
>>>>
>
>>>> What's wrong with my call ?
>
>>> Your call is Ok, but you have to set up your own NSAutoreleasePool.
>
>>> For
>
>>> your primary thread this is done by your application object, for
>
>>> Threads you have to do it on your own.
>
>>>
>
>>> NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
>
>>> The *pool destroys itself at the next opportunity, but releases all
>
>>> autoreleased objects before. so there is no [pool release].
>
>>>
>
>>> Stefan