107
技術社區[雲棲]
obj-c編程10:Foundation庫中類的使用(6)[線程和操作隊列]
任何語言都不能避而不談線程這個東東,雖然他是和平台相關的鳥,雖說unix哲學比較討厭線程的說...線程不是萬能靈藥,但有些場合還是需要的.談到線程就不得不考慮同步和死鎖問題,見如下代碼:
#import <Foundation/Foundation.h>
#define msg(...) NSLog(__VA_ARGS__)
@interface Foo:NSObject{
int i;
//NSLock *lock;
NSRecursiveLock *lock;
}
@property(atomic) int i;
@end
@implementation Foo
@synthesize i;
-(id)init{
self = [super init];
if(self){
i = 0;
//lock = [[NSLock alloc] init];
lock = [[NSRecursiveLock alloc] init];
}
return self;
}
-(void)loop{
int org_i = 0;
for(int x = 0;x < 5000;++x){
org_i = i;
[self inc];
//self.i++;
msg(@"%@:i has %d to %d",[[NSThread currentThread] name],\
org_i,i);
}
}
-(void)inc{
//@synchronized(self){
[lock lock];
i++;
[lock unlock];
//}
}
-(int)val{
return i;
}
@end
#define msg(...) NSLog(__VA_ARGS__)
int main(int argc,char *argv[])
{
@autoreleasepool{
id t = nil;
Foo *foo = [[Foo alloc] init];
NSMutableArray *ary = [[NSMutableArray alloc]init];
for(int i = 0;i<10;++i){
t = [[NSThread alloc] initWithTarget:foo selector:@selector(loop) \
object:nil];
[t setName:[NSString stringWithFormat:@"thread_%d",i]];
[ary addObject:t];
}
for(NSThread *t in ary){
[t start];
}
BOOL quit = NO;
while(true){
for(NSThread *t in ary){
quit = [t isFinished];
if(!quit) break;
}
if(quit) break;
[NSThread sleepForTimeInterval:1.0];
}
msg(@"at last val is %d",[foo val]);
}
return 0;
}
以上代碼涉及到線程的創建和同步代碼.除了通過線程實例方法initWithTarget:...方法創建以外還可以通過線程類方法+detachNewThreadSelector:...來創建,後一種方法創建後線程立即執行,而前一種方法需要使用-start方法啟動線程.線程的同步可以通過NSLock類的lock和unlock方法,或者可以使用obj-c的關鍵字:@synchronize來實現快速快速同步語義.對於類屬性來說,可以通過關鍵字atomic來聲明該屬性的原子性,不過實際執行好像不能確保同步,本貓哪裏沒考慮到呢?
我在使用NSURLConnection類獲取網絡數據的時候發現協議回調方法無法被回調,開始我以為可能回調需要時間執行,所以我在主線程中使用了循環+sleep函數等待其isFinished回調的完成.但是這個循環永遠也不會結束.後來發現NSURLConnection類的工作需要在主線程的"剩餘時間片"中完成,所以你sleep還是不行,等於主線程直接睡眠了不會分到時間片啊.這時你可以使用F庫中提供的NSRunLoop類的功能來完成:
[[NSRunLoop currentRunLoop] runUntilDate ...]
Cocoa GUI框架不是線程安全的,這意味著其他線程如果需要更新GUI組件必須通過主線程來實現,這可以通過NSObject實例方法-performSelectorOnMainThread:withObject:waitUntilDone:來實現;同樣的除了指定讓主線程做事之外,還可以指定讓其他後台線程上執行選擇器:-performSelector:onThread:waitIntilDone: .
線程是一種比較底層的OS提供的接口,obj-c提供了包裝線程的更高級的邏輯接口,這就是NSOperation和NSOperationQueue類,這種新型線程模型核心稱為GCD.你隻需把需要完成的任務放入NSOperationQueue隊列中,該隊列會根據係統cpu情況自動生成和銷毀一定數目的線程來完成工作,你就不用操心啦.你可以派生NSOperation類並覆寫其main方法來添加任務單元;你也可以使用F庫自帶的更方便的更專注於任務的NSInvocationOperation和NSBlockOperation類來描述任務單元.最後你可以使用-waitUntilAllOperationsAreFinished方法等待隊列中所有任務都完成.下麵我寫了一個簡單的利用隊列多線程完成任務的代碼,每一項任務單元就是給文件改名(但不改文件夾的名字),所有都在代碼裏嘍:
#import <Foundation/Foundation.h>
#define msg(...) printf("[%d]\n",__LINE__);NSLog(__VA_ARGS__)
@interface RenameConcur:NSObject{
NSString *path;
NSString *prefix;
}
@property NSString *path;
@property NSString *prefix;
-(id)initWithPath:(NSString*)path andPrefix:(NSString*)prefix;
-(void)renameWithPrefix;
@end
@implementation RenameConcur
@synthesize path,prefix;
-(id)initWithPath:(NSString*)path_v andPrefix:(NSString*)prefix_v{
self = [super init];
if(self){
path = [path_v stringByExpandingTildeInPath];
prefix = prefix_v;
}
return self;
}
-(id)init{
return [self initWithPath:nil andPrefix:nil];
}
-(void)renameWithPrefix{
if(!prefix || !path){
msg(@"obj's prefix or path is nil!");
}
else{
NSFileManager *fm = [NSFileManager defaultManager];
BOOL is_dir = NO;
if([fm fileExistsAtPath:path isDirectory:&is_dir] == NO){
msg(@"file %@ is not exist!",path);
return;
}else if(is_dir){
msg(@"path %@ is directory,do nothing!",path);
return;
}
NSString *just_name = [path lastPathComponent];
NSString *just_path = [path stringByDeletingLastPathComponent];
NSString *new_path = [NSString stringWithFormat:@"%@/%@%@",just_path,prefix,just_name];
if([fm moveItemAtPath:path toPath:new_path error:NULL] == NO){
msg(@"rename %@ to %@ failed!",path,new_path);
return;
}
}
}
@end
int main(int argc,char *argv[])
{
@autoreleasepool{
/*
NSString *path = @"~/src/objc_src/ddd.m";
NSString *prefix = @"love_";
RenameConcur *rnc = [[RenameConcur alloc] initWithPath:path andPrefix:prefix];
[rnc renameWithPrefix];
NSString *dir_path = [@"~/src/dir_love" stringByExpandingTildeInPath];
NSFileManager *fm = [NSFileManager defaultManager];
NSArray *files = [fm contentsOfDirectoryAtPath:dir_path error:NULL];
msg(@"files : %@",files);
for(NSString *file in files){
[rnc setPath:[NSString stringWithFormat:@"%@/%@",dir_path,file]];
[rnc renameWithPrefix];
}
*/
NSProcessInfo *process = [NSProcessInfo processInfo];
NSArray *args = [process arguments];
if([args count] != 3){
printf("usage : %s path prefix (notice : path's end don't with /)\n",\
[[[args objectAtIndex:0] lastPathComponent] \
cStringUsingEncoding:NSASCIIStringEncoding]);
return 1;
}
NSString *dir_path = [[args objectAtIndex:1] stringByExpandingTildeInPath];
NSString *prefix = [args objectAtIndex:2];
NSFileManager *fm = [NSFileManager defaultManager];
NSArray *files = [fm contentsOfDirectoryAtPath:dir_path error:NULL];
if(!files){
msg(@"get dir [%@] files's name failed!",dir_path);
return 2;
}
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
for(NSString *file in files){
RenameConcur *rnc_n = [[RenameConcur alloc] initWithPath:\
[NSString stringWithFormat:@"%@/%@",dir_path,file] andPrefix:prefix];
NSOperation *op = [[NSInvocationOperation alloc] initWithTarget:rnc_n \
selector:@selector(renameWithPrefix) object:nil];
[queue addOperation:op];
}
[queue waitUntilAllOperationsAreFinished];
}
return 0;
}
最後更新:2017-04-03 05:39:29