Objective-c线程和cpu性能

时间:2012-07-31 12:44:27

标签: objective-c multithreading macos cpu-usage

我有一个应用程序,可以在几个线程中从服务器下载一些文件。问题在于它给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

2 个答案:

答案 0 :(得分:2)

你不应该自己创建线程。为此,可以直接使用NSOperationQueue甚至GCD等专用API。他们更了解硬件限制,虚拟核心等,并支持优先级设置。

您也不应该使用+sendSynchronousRequest:。在charith建议的调度调用中包装-downloadChunk方法无助于提高性能,因为+sendSynchronousRequest:会阻塞线程直到新数据进入并强制GCD生成新线程。

使用委托回调使用NSURLConnection的异步API。您还可以将NSURLConnection代码包装在NSOperation子类中,并使用NSOperationQueue来管理下载:Using NSURLConnections

如果您不想自己编写NSOperation子类,也可以使用AFNetworking之类的第三方框架。

答案 1 :(得分:0)

尝试使用GCD块和全局队列。这是苹果推荐的并发方式:

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

        [self downloadChunk:objDownload];
    });