您的位置:首页 > 其它

多线程中的timer

2015-12-29 18:43 267 查看
目录

1. NSRunLoopCommonModes和Timer

2. NSThread和Timer

3. GCD中的Timer

 
返回目录

1. NSRunLoopCommonModes和Timer

    当使用
NSTimer
scheduledTimerWithTimeInterval
方法时。事实上此时Timer会被加入到当前线程的Run
Loop中,且模式是默认的
NSDefaultRunLoopMode
。而如果当前线程就是主线程,也就是UI线程时,某些UI事件,比如
UIScrollView
的拖动操作,会将Run
Loop切换成
NSEventTrackingRunLoopMode
模式,在这个过程中,默认的
NSDefaultRunLoopMode
模式中注册的事件是不会被执行的。也就是说,此时使用
scheduledTimerWithTimeInterval
添加到Run
Loop中的Timer就不会执行。
4000

 

    所以为了设置一个不被UI干扰的Timer,我们需要手动创建一个Timer,然后使用
NSRunLoop
addTimer:forMode:
方法来把Timer按照指定模式加入到Run
Loop中。这里使用的模式是:
NSRunLoopCommonModes
,这个模式等效于
NSDefaultRunLoopMode
NSEventTrackingRunLoopMode
的结合。(参考Apple文档

参考代码:

- (void)viewDidLoad
{
[super viewDidLoad];

NSLog(@"主线程 %@", [NSThread currentThread]);
//创建Timer
NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(timer_callback) userInfo:nil repeats:YES];
//使用NSRunLoopCommonModes模式,把timer加入到当前Run Loop中。
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}

//timer的回调方法
- (void)timer_callback
{
NSLog(@"Timer %@", [NSThread currentThread]);
}


 

输出:

主线程 <NSThread: 0x71501e0>{name = (null), num = 1}
Timer <NSThread: 0x71501e0>{name = (null), num = 1}
Timer <NSThread: 0x71501e0>{name = (null), num = 1}
Timer <NSThread: 0x71501e0>{name = (null), num = 1}


 
返回目录

2. NSThread和Timer

    上面讲的
NSRunLoopCommonModes
和Timer中有一个问题,这个Timer本质上是在当前线程的Run
Loop中循环执行的,因此Timer的回调方法不是在另一个线程的。那么怎样在真正的多线程环境下运行一个Timer呢?

    可以先试试
NSThread
。同上,我们还是会把Timer加到Run
Loop中,只不过这个是在另一个线程中,因此我们需要手动执行Run Loop(通过
NSRunLoop
run
方法),同时注意在新的线程执行中加入
@autoreleasepool


完整代码如下:

- (void)viewDidLoad
{
[super viewDidLoad];

NSLog(@"主线程 %@", [NSThread currentThread]);

//创建并执行新的线程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(newThread) object:nil];
[thread start];
}

- (void)newThread
{
@autoreleasepool
{
//在当前Run Loop中添加timer,模式是默认的NSDefaultRunLoopMode
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(timer_callback) userInfo:nil repeats:YES];
//开始执行新线程的Run Loop
[[NSRunLoop currentRunLoop] run];
}
}

//timer的回调方法
- (void)timer_callback
{
NSLog(@"Timer %@", [NSThread currentThread]);
}


 

输出:

主线程 <NSThread: 0x7118800>{name = (null), num = 1}
Timer <NSThread: 0x715c2e0>{name = (null), num = 3}
Timer <NSThread: 0x715c2e0>{name = (null), num = 3}
Timer <NSThread: 0x715c2e0>{name = (null), num = 3}


 
返回目录

3. GCD中的Timer

GCD中的Timer应该是最灵活的,而且是多线程的。GCD中的Timer是靠Dispatch Source来实现的。

因此先需要声明一个
dispatch_source_t
本地变量:

@interface ViewController ()
{
dispatch_source_t _timer;
}


 

接着通过
dispatch_source_create
函数来创建一个专门的Dispatch
Source,接着通过
dispatch_source_set_timer
函数来设置Timer的参数,注意这里的时间参数有些蛋疼。

开始时间的类型是
dispatch_time_t
,最好用
dispatch_time
或者
dispatch_walltime
函数来创建
dispatch_time_t
对象。如果需要Timer立即执行,可以传入
dispatch_time(DISPATCH_TIME_NOW,
0)


internal
leeway
参数分别表示Timer的间隔时间和精度。类型都是
uint64_t
。间隔时间的单位竟然是纳秒。可以借助预定义的
NSEC_PER_SEC
宏,比如如果间隔时间是两秒的话,那
interval
参数就是:
2
* NSEC_PER_SEC


leeway
就是精度参数,代表系统可以延时的时间间隔,最高精度当然就传0。

然后通过
dispatch_source_set_event_handler
函数来设置Dispatch
Source的事件回调,这里当然是使用Block了。

最后所有
dispatch_source_t
创建后默认都是暂停状态的,所以必须通过
dispatch_resume
函数来开始事件监听。这里就代表着开始Timer。

 

完整代码:

NSLog(@"主线程 %@", [NSThread currentThread]);
//间隔还是2秒
uint64_t interval = 2 * NSEC_PER_SEC;
//创建一个专门执行timer回调的GCD队列
dispatch_queue_t queue = dispatch_queue_create("my queue", 0);
//创建Timer
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
//使用dispatch_source_set_timer函数设置timer参数
dispatch_source_set_timer(_timer, dispatch_time(DISPATCH_TIME_NOW, 0), interval, 0);
//设置回调
dispatch_source_set_event_handler(_timer, ^()
{
NSLog(@"Timer %@", [NSThread currentThread]);
});
//dispatch_source默认是Suspended状态,通过dispatch_resume函数开始它
dispatch_resume(_timer);


 

输出:

主线程 <NSThread: 0x711fab0>{name = (null), num = 1}
Timer <NSThread: 0x713a380>{name = (null), num = 3}
Timer <NSThread: 0x713a380>{name = (null), num = 3}
Timer <NSThread: 0x713a380>{name = (null), num = 3}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: