Ce forum est en partie financé par l’affichage de publicités. Merci de désactiver votre bloqueur de publicités pour nous permettre de continuer à fournir ce service.

NSTask et utilisation CPU

Discussion dans 'Développement Mac' créé par Ludopac, 9 Juillet 2003.

  1. Ludopac

    Ludopac Membre émérite

    Inscrit:
    5 Avril 2001
    Messages:
    970
    J'aime reçus:
    4
    J'ai un problème avec la class NSTask. Je voudrais en fait faire une interface graphique pour un logiciel UNIX.

    Pas de problème si c'est un programme qui renvoie un certain nombre de ligne et qui se termine (genre ls, ps, grep ...).

    Mais dans mon cas, c'est ffmpeg, logiciel d'encodage qui envoie en continue des données.

    J'arrive à récupérer les données que ffmpeg transmet au fur et à mesure et récupérer ce qui m'intéresse, mais ce mécanisme occupe 80 % du processeur ne laissant que 20 % à ffmpeg.

    Le problème vient certainement du fait que les données arrivent en continue et très vite. Donc je me demandais si il n'y avait pas moyen de lancer ffmpeg par NSTask et ensuite d'aller chercher les données que toutes les 2 secondes par exemple.

    Voilà le code que j'utilise (ça vient de Cocoa Dev Central) ...
    Ce qui occupe toutes les ressources, c'est la fonction copyData:.
    C'est une boucle qui lit en permanence les données transmises par ffmpeg et les recopie dans un champ de texte.

    Pour régler le problème, je pensais faire en sorte que ce qui se trouve dans la boucle ne soit exécuté qu'une fois toutes les 1 ou 2 secondes. Mais est-ce possible ? Ou dois-je procéder autrement ?

    Je débute totalement en Objectif C donc si quelqu'un pouvait m'aider, ça serait sympa parce là je suis perdu [​IMG]
     
  2. Manu

    Manu Membre d’élite
    Club MacG

    Inscrit:
    31 Mai 2000
    Messages:
    1 743
    J'aime reçus:
    204
    Si tu veux que ta boucle soit exécutée toutes les deux secondes, tu dois la declarer dans une méthode comme copyData et tu dois créer un timer par
    NSTimer *myTimer=[NSTimer timerWithTimeInterval:2 target:self selector: @selector(copyData:) userInfo:nil repeats:YES];

    ce timer se déclenchera toutes les 2 secondes en exécutant de manière répétée la methode copyData.

    Une façon plus élégante de résoudre ton problème c'est d'utiliser l'une des notifications envoyées par NSFileHandle en se déclarant comme observer de cette notification et en spécifiant au NotificationCenter d'exécuter la méthode copyData à chaque fois qu'il y a des données à récupérer. C'est l'une des forces du NSFileHandler.
     
  3. Ludopac

    Ludopac Membre émérite

    Inscrit:
    5 Avril 2001
    Messages:
    970
    J'aime reçus:
    4
    Pourrais tu me donner un exemple rapide de la façon dont s'implémenterait cette solution, parce que j'ai fait un essai, et ça me fait pareille. 80% du proc pour l'interface et 20 % pour ffmpeg.

    Le truc, c'est que ffmpeg envoie réellement beaucoup de données et trés vite. [​IMG]

    En tout cas merci pour ton aide.
     
  4. Ludopac

    Ludopac Membre émérite

    Inscrit:
    5 Avril 2001
    Messages:
    970
    J'aime reçus:
    4
    C'est bon j'ai réussi avec les notifications comme tu me l'as indiqué ...

    C'est vraiment cool, maintenant j'ai 10-20% pour l'interface et 90-80 pour ffmepg, ce qui est pas mal du tout [​IMG]

    Un grand merci pour ton aide [​IMG]
     
  5. Manu

    Manu Membre d’élite
    Club MacG

    Inscrit:
    31 Mai 2000
    Messages:
    1 743
    J'aime reçus:
    204
    Content que t'ai compris l'utilité des NotificationCenter. L'autre truc très cool à connaitre pour bien comprendre cocoa c'est la Delegation, puis les protocoles et les categories.

    Salut
     
  6. Ludopac

    Ludopac Membre émérite

    Inscrit:
    5 Avril 2001
    Messages:
    970
    J'aime reçus:
    4
    Merci pour les conseils. Les notifications j'utilise déjà, et ça rend bien service. Les catégories je connais aussi, faudra juste que je regarde ce que sont les protocoles [​IMG]


     
  7. Manu

    Manu Membre d’élite
    Club MacG

    Inscrit:
    31 Mai 2000
    Messages:
    1 743
    J'aime reçus:
    204
    Les protocoles c'est ce que les développeurs Java appellent Interface. Pour ma part je trouve que le terme protocole est plus approprié. C'est le fait de rajouter à une classe existante un certain nombre de méthodes sans les implémenter.
    Quand tu lis la description d'une Classe dans Foundation Kit ou Application Kit, tu remarques qu'au début il y a les annotations semblables à celles de la classe NSMutableString décrits comme suit :

    Inherits from: NSString : NSObject
    Conforms to: NSCoding (NSString)
    NSCopying (NSString)
    NSMutableCopying (NSString)
    NSObject (NSObject)

    les inherits c'est les classes mères, alors que Conforms to indique la liste des protocoles auxquels se conforme la classe.
    Par exemple le fait que la classe obeit au potocole NSCopying cela veut dire que tu peux faire des copies d'une instance de la classe.
    Ainsi si tu crées une Classe, si tu veux qu'on puisse faire des copies des objets de ta classe, il faut que ta classe obeisse au protocole NSCopying. Voila comment tu la déclares :
    @interface myClasse : NSObject <NSCopying> {
    attributs de ta classe
    }
    methodes de ta classe
    @end
    dans l'implémentation de tes méthodes, tu implémntes la méthode copyWithZone.
    Ainsi si ta classe s'appelle MyClasse, on pourra faire
    MyClasse *myCopy = [truc copy] la methode copy utilise la méthode copyWithZone du protocole NSCopying qu tu as implémentée. En fait la copie consiste a copier un à un les attributs de ton objet.
    J'espère qu j'ai été clair. Va dans les sample fournis par Apple tu dois trouver des exemples d'implémentation.

     
  8. Ludopac

    Ludopac Membre émérite

    Inscrit:
    5 Avril 2001
    Messages:
    970
    J'aime reçus:
    4
    Merci pour les explications [​IMG] J'vais jeter un oeil sur des exemples pour voir tout ça en pratique ...

    En tout cas c'est sympa d'avoir des conseils d'un spécialise [​IMG]
     
  9. Manu

    Manu Membre d’élite
    Club MacG

    Inscrit:
    31 Mai 2000
    Messages:
    1 743
    J'aime reçus:
    204
    Pendant qu'on y est on va aller plus loin. En fait les protocoles permettent d'implémenter le polymorphisme. Si tu ne le sais pas, le polymorphisme c'est le fait d'avoir un nom de methode utilisé par des classes différentes pour exécuter une même fonction. Mais l'implémentation de cette méthode dépend de la classe. Exemple si j'ai deux objets différents, la fonction copy ne s'exécute pas de la même manière car ils ont des attributs différents. Hors une copie c'est ni plus ni moins une duplication d'attributs.
    Dans la Foundation Kit et l'Application Kit , après la liste des classes, il y a celle des protocoles.
    Généralement les protocoles sont utilisés dans les objets de base ou objets modèle. Le terme modèle est celui du paradigme MVC (Model View Controller) qui est le schéma directeur de toute application Cocoa.
    Il y a des protocoles qui sont très interresants. Par exemple le protocol NSCoding (tu remarqueras la terminaison ing des protocol qui veut dire possibilité de) definit deux méthodes initWithCoder et encodeWithCoder. Si ton objet obeit à ce protocole, cela veut dire que des instances de cet objet peuvent être archivées dans un fichier ou une base de données avec encodeWithCoder, puis récupérés du fichier et reconstruit avec initWithCoder.
    Donc des objets archivables.
    L'autre protocol interressant c'est NSKeyValueCoding. Il est très important car si tous les objets de ton appli obeissent à ce protocole, ton application est très facilement scriptable. En effet AppleScript utilise énormément les méthodes de ce protocole pour retouver des objets d'après la valeur d'un attribut. ( exemple la 3ème cellule de la seconde ligne).
    La force de Cocoa c'est d'offrir des Framework (Foundation Application Kit) avec des éléments de bases très fournis et donnant beaucoup de possibilité et de souplesse pour développer rapidement et surtout pour obtenir les résultats qu'on attend.
     
  10. plumber

    plumber Membre confirmé

    Inscrit:
    15 Janvier 2003
    Messages:
    473
    J'aime reçus:
    0
    arreter tous

    tu as besoin de piper ton stream c'est tout

    tu pipes donc ton process

    et tu while true
    c'est la meme chose que pour piper un netstat -w1

    tu lis le stream c'est tout est quand tu en as marre tu referme le pipe
    [​IMG]


    arretes manu tu vas lui faire peur

    #import "activityController.h"

    // private methods
    @interface activityController(Private)
    - (void)start;
    - (void)stop;
    - (void)output:(NSNotification *)aNotification;
    - (void)error:(NSNotification *)errNotification;
    @end

    @implementation activityController

    - (IBAction)startStop:(id)sender
    {
    if (theTask) {
    [self stop];
    } else {
    [self start];
    }
    }

    - (void)start
    {
    // initialize arguments for CLI -
    // poll every second for network stats
    NSMutableArray *theArguments = [NSMutableArray arrayWithArray:[NSArray arrayWithObjects:mad:"-w1", nil]];

    // open a pipe for netstat to send its output to
    NSPipe *thePipe = [NSPipe pipe];

    // open another pipe for the errors
    NSPipe *errorPipe = [NSPipe pipe];

    // make sure there isn't netstat task currently running
    NSAssert( !theTask, @"Task already exists" );

    // set sent packet widget at 0, received at 0
    [outboundProgressIndicator setDoubleValue:0];
    [inboundProgressIndicator setDoubleValue:0];

    // initialize storage
    totalSeconds = 0;
    totalIn = 0;
    totalOut = 0;

    // clear data fields
    [inboundCurrent setIntValue:0];
    [inboundAvg setIntValue:0];
    [inboundMax setIntValue:0];
    [outboundCurrent setIntValue:0];
    [outboundAvg setIntValue:0];
    [outboundMax setIntValue:0];

    // create the subprocess
    theTask = [[NSTask alloc] init];

    // set the subprocess to start a netstat session
    [theTask setLaunchPath:mad:"/usr/sbin/netstat"];

    // set up arguments for netstat CLI
    [theTask setArguments:theArguments];

    // point the output of the netstat session to the pipe,
    // and the error to the other pipe
    [theTask setStandardOutput:thePipe];
    [theTask setStandardError:errorPipe];

    // launch netstat
    [theTask launch];

    // make sure I haven't already got an output object
    NSAssert( !theOutput, @"Output already exists" );

    // create file handles for the output and error
    theOutput = [[thePipe fileHandleForReading] retain];
    theError = [[errorPipe fileHandleForReading] retain];

    // register myself as an observer for this pipe
    // updates trigger output method
    [[NSNotificationCenter defaultCenter] addObserver:self selector:mad:selector(output:) name:NSFileHandleReadCompletionNotification object:theOutput];

    // register myself as an observer for the error pipe
    // updates trigger error method
    [[NSNotificationCenter defaultCenter] addObserver:self selector:mad:selector(error:) name:NSFileHandleReadCompletionNotification object:theError];

    // put it in the background - don't hold UI hostage
    [theOutput readInBackgroundAndNotify];
    [theError readInBackgroundAndNotify];

    // make the UI signal user - session in progress
    // change button to stop
    [theButton setTitle:mad:"Stop"];

    // this thing should be purring like a kitten
    }

    - (void)stop
    {
    // kill the subprocess, clear from memory
    [theTask interrupt];
    theTask = nil;

    // unregister for notification
    [[NSNotificationCenter defaultCenter] removeObserver:self name:NSFileHandleReadCompletionNotification object:theOutput];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:NSFileHandleReadCompletionNotification object:theError];

    // clear the output object from memory
    [theOutput release];
    [theError release];
    theOutput = nil;
    theError = nil;

    // reset the UI
    [theButton setTitle:mad:"Start"];
    }

    - (void)output:(NSNotification *)aNotification
    {
    // get the data from the notification
    NSData *theData = [[aNotification userInfo] objectForKey:NSFileHandleNotificationDataItem];

    NSString *theString = [[[NSString alloc] initWithData:theData encoding:NSNEXTSTEPStringEncoding] autorelease];

    int thisInbound = 0;
    int thisOutbound = 0;
    float outPercent = 0;
    float inPercent = 0;
    int avgIn = 0;
    int avgOut = 0;

    // set up a scanner to fish out the numbers
    NSScanner *theScanner = [NSScanner scannerWithString:theString];

    // increment totalSeconds for averaging purposes
    totalSeconds = totalSeconds + 1;

    // strip out headers
    [theScanner scanUpToCharactersFromSet:[NSCharacterSet decimalDigitCharacterSet] intoString:nil];

    if([theScanner isAtEnd] == NO){
    // the first two number are to be skipped
    [theScanner scanInt:NULL];
    [theScanner scanInt:NULL];


    // the next number is the incoming bytes
    [theScanner scanInt:&thisInbound];

    // skip two more numbers
    [theScanner scanInt:nil];
    [theScanner scanInt:nil];

    // the next number is the outgoing bytes
    [theScanner scanInt:&thisOutbound];

    // see if inbound has exceeded max
    if([inboundMax intValue] < thisInbound){
    [inboundMax setIntValue:thisInbound];
    }

    // see if outbound has exceeded max
    if([outboundMax intValue] < thisOutbound){
    [outboundMax setIntValue:thisOutbound];
    }

    // put current numbers into UI
    [outboundCurrent setIntValue:thisOutbound];
    [inboundCurrent setIntValue:thisInbound];

    // calculate percentage - don't divide by zero
    if([inboundMax intValue] > 0){
    inPercent = (thisInbound * 100) / [inboundMax intValue];
    } else {
    inPercent = 0;
    }

    if([outboundMax intValue] > 0){
    outPercent = (thisOutbound * 100) / [outboundMax intValue];
    } else {
    outPercent = 0;
    }

    // set the progress bars
    [outboundProgressIndicator setDoubleValue:eek:utPercent];
    [inboundProgressIndicator setDoubleValue:inPercent];

    // add in and out to accumulated total
    totalIn = totalIn + thisInbound;
    totalOut = totalOut + thisOutbound;

    // calculate average
    avgIn = (totalIn / totalSeconds);
    avgOut = (totalOut / totalSeconds);

    // set average fields
    [outboundAvg setIntValue:avgOut];
    [inboundAvg setIntValue:avgIn];

    }

    // reset the notifier
    [theOutput readInBackgroundAndNotify];
    }

    - (void)error:(NSNotification *)errNotification
    {
    // get the data from the notification
    NSData *errData = [[errNotification userInfo] objectForKey:NSFileHandleNotificationDataItem];

    NSString *errString = [[[NSString alloc] initWithData:errData encoding:NSNEXTSTEPStringEncoding] autorelease];

    // tell the user what the problem is
    NSBeginAlertSheet( @"Bad News", @"Ok", nil, nil, [theApp keyWindow], nil, NULL, NULL, NULL, errString);

    // reset progress meters, this run was a dud
    [inboundProgressIndicator setDoubleValue:0];
    [outboundProgressIndicator setDoubleValue:0];

    // we're done
    [self stop];
    }

    @end
     
  11. Ludopac

    Ludopac Membre émérite

    Inscrit:
    5 Avril 2001
    Messages:
    970
    J'aime reçus:
    4
    Non, mais c'est bon j'ai dit que j'avais réussi avec les notifications [​IMG]

    Mais merci quand même pour tout ce code [​IMG]
     
  12. plumber

    plumber Membre confirmé

    Inscrit:
    15 Janvier 2003
    Messages:
    473
    J'aime reçus:
    0
Chargement...