iOS 多線程下安全的使用定時器

iOS中timer相關的延時調用,常見的有NSObject中的performSelector:withObject:afterDelay:這個方法在調用的時候會設置當前runloop中timer,還有一種延時,直接使用NSTimer來配置任務。xcode

    這兩種方式都一個共同的前提,就是當前線程裏面須要有一個運行的runloop而且這個runloop裏面有一個timer。安全

    咱們知道:只有主線程會在建立的時候默認自動運行一個runloop,而且有timer,普通的子線程是沒有這些的。這樣就帶來一個問題了,有些時候咱們並不肯定咱們的模塊是否是會異步調用到,而咱們在寫這樣的延時調用的時候通常都不會去檢查運行時的環境,這樣在子線程中被調用的時候,咱們的代碼中的延時調用的代碼就會一直等待timer的調度,可是實際上在子線程中又沒有這樣的timer,這樣咱們的代碼就永遠不會被調到。多線程

    下面的代碼展現了performSelector和dispatch_time的不一樣併發

  1. /* 
  2.  採用gcd的方式 延時添加到隊列 
  3.  */  
  4. -(void) testDispatch_after{  
  5.     dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3*NSEC_PER_SEC);  
  6.     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);  
  7.     dispatch_after(time, queue, ^{  
  8.         NSLog(@"3秒後添加到隊列");  
  9.     });  
  10.     dispatch_release(queue);  
  11. }  
  12. -(void) testDelay{  
  13.     NSLog(@"testDelay被執行");  
  14. }  
  15. /* 
  16.  dispatch_barrier_async 柵欄的做用 
  17.  */  
  18. -(void) testDispatch_Barrier{  
  19.     //dispatch_queue_t gcd = dispatch_queue_create("這是序列隊列", NULL);  
  20.     dispatch_queue_t gcd = dispatch_queue_create("這是併發隊列", DISPATCH_QUEUE_CONCURRENT);  
  21.     dispatch_async(gcd, ^{  
  22.         NSLog(@"b0");  
  23.         //這個selector不會執行,由於線程中沒有runloop  
  24.         [self performSelector:@selector(testDelay) withObject:nil afterDelay:3];  
  25.         //代碼會執行,由於採用了gcd方式  
  26.         [self testDispatch_after];  
  27.     });  
  28.     dispatch_release(gcd);  
  29. }  

 

    在有多線程操做的環境中,這樣performSelector的延時調用,實際上是缺少安全性的。咱們能夠用另外一套方案來解決這個問題,就是使用GCD中的dispatch_after來實現單次的延時調用異步

解決方案一:

performSelector並非沒有辦法保證線程安全。例以下面的代碼就能夠運行:async

  1. [self performSelector:@selector(testDelay) onThread:[NSThread mainThread] withObject:nil waitUntilDone:NO];  

指定了該selector在主線程中運行。oop

解決方案二:

  1. [self performSelector:@selector(testDelay) withObject:nil afterDelay:3 inModes:[NSArray arrayWithObject:NSDefaultRunLoopMode]];  
  2.   [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];  

啓動線程中runloop,由於每一個線程就有個默認的runloopspa

解決方案三:線程

      1. [self performSelector:@selector(printLog) withObject:nil afterDelay:2.0f inModes:[NSArray arrayWithObject:NSDefaultRunLoopMode]];code

      2.[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];

相關文章
相關標籤/搜索