obj-c编程10:Foundation库中类的使用(6)[线程和操作队列]

来源:互联网 发布:练字软件免费下载 编辑:程序博客网 时间:2024/06/05 10:10

    任何语言都不能避而不谈线程这个东东,虽然他是和平台相关的鸟,虽说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;}}}@endint 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;}

0 0
原创粉丝点击