1

いくつかのスレッドでサーバーからいくつかのファイルをダウンロードするアプリがあります。問題は、CPUに大きな負荷(80%に達する)を与えていることです。それをより良くするために何ができるでしょうか?WindowsでC#を使用して同様のアプリを作成しましたが、CPU使用率が5%を超えることはありません。

編集:このコードは、以下のいくつかの提案を得た後に変更されました。ここでの問題は、[queue setMaxConcurrentOperationCount:6]を設定したときにダウンロードが100%に到達しないことです。非同期NSURLConnectionをsendSynchronous呼び出しに戻すと、それが機能します。上記のOperationCountを1に変更すると、機能します。

これは、NSOperationsをキューに追加する方法です(800のように大きい場合があります)。

int chunkId = 0;
for (DownloadFile *downloadFile in [download filesInTheDownload])
{
    chunkId = 0;
    for (DownloadChunk *downloadChunk in [downloadFile chunksInTheFile])
    {
        DownloadChunkOperation *operation = [[DownloadChunkOperation alloc]  initWithDownloadObject:download
      downloadFile:downloadFile                                                                                                    downloadChunk:downloadChunk                                                                                                           andChunkId:chunkId];
    [queue addOperation:operation];
    chunkId++;
    }
}


#import "DownloadChunkOperation.h"
#import "Download.h"
#import "DownloadFile.h"
#import "DownloadChunk.h"

@interface DownloadChunkOperation()
@property(assign) BOOL isExecuting;
@property(assign) BOOL isFinished;
@end

@implementation DownloadChunkOperation

@synthesize download = _download;
@synthesize downloadFile = _downloadFile;
@synthesize downloadChunk = _downloadChunk;

@synthesize isFinished = _isFinished;
@synthesize isExecuting = _isExecuting;

- (id) initWithDownloadObject:(Download *)download downloadFile:(DownloadFile *)downloadFile downloadChunk:(DownloadChunk *)downloadChunk andChunkId:(uint32_t)chunkId
{
    self = [super init];

    if (self) {
        self.download = download;
        self.downloadFile = downloadFile;
        self.downloadChunk = downloadChunk;
        self.chunkId = chunkId;
    }

    return self;
}

- (void) start
{
    if ([self isCancelled]) {
        [self setIsFinished:YES];
        [self setIsExecuting:NO];
        return;
    }

    [self setIsExecuting:YES];
    [self setIsFinished:NO];
    [self.downloadChunk setChunkState:cDownloading];

    downloadPath = [[NSString stringWithFormat:@"%@/%@", [self.download downloadFolder], [self.download escapedTitle]] stringByExpandingTildeInPath];

    NSURL *fileURL = [[NSURL alloc] initWithString:[self.downloadFile filePath]];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:fileURL];
    NSString *range = [NSString stringWithFormat:@"bytes=%lli-%lli", [self.downloadChunk startingByte], [self.downloadChunk endingByte]];
    [request setValue:range forHTTPHeaderField:@"Range"];
    connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
    // IMPORTANT! The next line is what keeps the NSOperation alive for the during of the NSURLConnection!
    [connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [connection start];

    if (connection) {
        NSLog(@"connection established!");
        do {
            [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
        } while (!self.isFinished);
    } else {
        NSLog(@"couldn't establish connection for: %@", fileURL);
    }
}

- (BOOL) isConcurrent
{
    return YES;
}

- (void) connection:(NSURLConnection *)_connection didReceiveResponse:(NSURLResponse *)response
{
    receivedData = [[NSMutableData alloc] init];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    // Not cancelled, receive data.
    if (![self isCancelled]) {
        [receivedData appendData:data];
        self.download.downloadedBytes += [data length];
        return;
    }

    // Cancelled, tear down connection.
    [self setIsExecuting:NO];
    [self setIsFinished:YES];
    [self.downloadChunk setChunkState:cConnecting];
    [self->connection cancel];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    [self setIsExecuting:NO];
    [self setIsFinished:YES];
    NSLog(@"Connection failed! Error - %@ %@",
          [error localizedDescription],
          [[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    NSString *chunkPath = [downloadPath stringByAppendingFormat:@"/%@.%i", [self.downloadFile fileName], self.chunkId];
    NSError *saveError = nil;
    [receivedData writeToFile:chunkPath options:NSDataWritingAtomic error:&saveError];
    if (saveError != nil) {
        NSLog(@"Download save failed! Error: %@", [saveError description]);
    }
    else {
        NSLog(@"file has been saved!: %@", chunkPath);
    }
    [self setIsExecuting:NO];
    [self setIsFinished:YES];
    [self.downloadChunk setChunkState:cFinished];

    if ([self.download downloadedBytes] == [self.download size])
        [[NSNotificationCenter defaultCenter] postNotificationName:@"downloadFinished" object:self.download];
}

@end
4

2 に答える 2

2

You should not create threads yourself. Use dedicated API like NSOperationQueue or even GCD directly for this purpose. They know better about hardware limits, virtual cores, etc. and support priority settings.

You shouldn't use +sendSynchronousRequest: either. Wrapping your -downloadChunk method in a dispatch call as suggested by charith won't help you improve performance, as +sendSynchronousRequest: blocks the thread until new data comes in and forces GCD to spawn new threads.

Use the asynchronous API of NSURLConnection using delegate callbacks. You can also wrap your NSURLConnection code inside a NSOperation subclass and use NSOperationQueue to manage the downloads: Using NSURLConnections

If you don't want to write the NSOperation subclass yourself, you can also use a 3rd party framework like AFNetworking.

于 2012-07-31T12:58:34.757 に答える
0

GCDブロックとグローバルキューを試してみてください。これは、並行性のために現在アップルが推奨する方法です。例:

dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(globalQueue, ^{

        [self downloadChunk:objDownload];
    });
于 2012-07-31T12:54:17.410 に答える