我有一个应用程序可以在几个线程中从服务器下载一些文件。问题是它给 CPU 带来了沉重的负载(达到 80%)。可以做些什么来让它变得更好?我用 C# 在 Windows 上制作了类似的应用程序,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