iOS多線程

GCD

  • 同步/異步 和 串行/ 并發(fā)
  • dispatch_barrier_async
  • dispatch_group

同步/異步 和 串行/ 并發(fā)

  • dispatch_sync(serial_queue, ^{//任務(wù)})妹蔽;
  • dispatch_async(serial_queue, ^{//任務(wù)})乍楚;
  • dispatch_sync(concurrent_queue, ^{//任務(wù)})丽柿;
  • dispatch_async(concurrent_queue, ^{//任務(wù)});

同步串行

//頭條面試題
- (void)viewDidLoad {
    [super viewDidLoad];
    dispatch_sync(dispatch_get_main_queue(), ^{
        [self doSomething];
    });
}

上面代碼的問題:
這段代碼的邏輯會(huì)產(chǎn)生死鎖甚牲,死鎖的原因隊(duì)列引起的循環(huán)等待.

隊(duì)列循環(huán)等待.png

viewDidLoad的執(zhí)行過程中需要依賴于調(diào)用Block任務(wù)璃弄,而Block在隊(duì)列中的排列(即棧的先進(jìn)先出,因?yàn)锽lock任務(wù)在棧頂)導(dǎo)致他需要依賴viewDidLoad執(zhí)行完畢窜觉,這種彼此依賴對方先完成谷炸,就導(dǎo)致了死鎖發(fā)生。

- (void)viewDidLoad {
    [super viewDidLoad];
    dispatch_sync(serialQueue, ^{
        [self doSomething];
    });
}

代碼執(zhí)行正常禀挫,沒有問題:

同步串行02.png
//美團(tuán)
- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"1");
    dispatch_sync(global_queue, ^{
        NSLog(@"2");
        dispatch_sync(global_queue, ^{
            NSLog(@"3");
        });
        NSLog(@"4");
    });
    NSLog(@"5");
}//12345

只要同步去提交任務(wù)旬陡,無論隊(duì)列是串行還是并發(fā),最終都會(huì)在當(dāng)前線程去執(zhí)行

異步并發(fā)

//騰訊視頻
- (void)viewDidLoad {
    [super viewDidLoad];
    dispatch_async(global_queue, ^{
        NSLog(@"1");
        [self performSelector:@selector(printLog) withObject:nil afterDelay:0];
        NSLog(@"3");
    });
  //輸出結(jié)果13
}
- (void)printLog {
    NSLog(@"2");
}

performSelector:withObject:afterDelay:因?yàn)樾枰?dāng)前線程有runloop去執(zhí)行timer事件特咆,但GCD底層是不創(chuàng)建runloop的沒有runloop季惩,即使時(shí)間是0录粱,方法也會(huì)失效腻格。所以結(jié)果是13

dispatch_barrier_async()

隊(duì)列循環(huán)等待.png

怎樣利用GCD實(shí)現(xiàn)多讀單寫?(滴滴啥繁,美團(tuán)面試題)

多讀單寫.png
#import "UserCenter.h"

@interface UserCenter()
{
    // 定義一個(gè)并發(fā)隊(duì)列
    dispatch_queue_t concurrent_queue;
    
    // 用戶數(shù)據(jù)中心, 可能多個(gè)線程需要數(shù)據(jù)訪問
    NSMutableDictionary *userCenterDic;
}

@end

// 多讀單寫模型
@implementation UserCenter

- (id)init
{
    self = [super init];
    if (self) {
        // 通過宏定義 DISPATCH_QUEUE_CONCURRENT 創(chuàng)建一個(gè)并發(fā)隊(duì)列
        concurrent_queue = dispatch_queue_create("read_write_queue", DISPATCH_QUEUE_CONCURRENT);
        // 創(chuàng)建數(shù)據(jù)容器
        userCenterDic = [NSMutableDictionary dictionary];
    }
    
    return self;
}

- (id)objectForKey:(NSString *)key
{
    __block id obj;
    // 同步讀取指定數(shù)據(jù)
    dispatch_sync(concurrent_queue, ^{
        obj = [userCenterDic objectForKey:key];
    });
    
    return obj;
}

- (void)setObject:(id)obj forKey:(NSString *)key
{
    // 異步柵欄調(diào)用設(shè)置數(shù)據(jù)
    dispatch_barrier_async(concurrent_queue, ^{
        [userCenterDic setObject:obj forKey:key];
    });
}

@end

dispatch_group_async()

使用GCD實(shí)現(xiàn)三個(gè)需求:A菜职、B、C三個(gè)任務(wù)并發(fā)旗闽,完成后執(zhí)行任務(wù)D?(愛奇藝面試題)

#import "GroupObject.h"

@interface GroupObject()
{
    dispatch_queue_t concurrent_queue;
    NSMutableArray <NSURL *> *arrayURLs;
}

@end

@implementation GroupObject

- (id)init
{
    self = [super init];
    if (self) {
        // 創(chuàng)建并發(fā)隊(duì)列
        concurrent_queue = dispatch_queue_create("concurrent_queue", DISPATCH_QUEUE_CONCURRENT);
        arrayURLs = [NSMutableArray array];
    }

    return self;
}

- (void)handle
{
    // 創(chuàng)建一個(gè)group
    dispatch_group_t group = dispatch_group_create();
    
    // for循環(huán)遍歷各個(gè)元素執(zhí)行操作
    for (NSURL *url in arrayURLs) {
        
        // 異步組分派到并發(fā)隊(duì)列當(dāng)中
        dispatch_group_async(group, concurrent_queue, ^{
            
            //根據(jù)url去下載圖片
            
            NSLog(@"url is %@", url);
        });
    }
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 當(dāng)添加到組中的所有任務(wù)執(zhí)行完成之后會(huì)調(diào)用該Block
        NSLog(@"所有圖片已全部下載完成");
    });
}
@end

NSOperation

需要和NSOperationQueue配合使用來實(shí)現(xiàn)多線程方案

優(yōu)勢和特點(diǎn):

  • 添加任務(wù)依賴
  • 任務(wù)執(zhí)行狀態(tài)控制
  • 最大并發(fā)量

任務(wù)執(zhí)行狀態(tài)控制

  • isReady - 當(dāng)前任務(wù)是否就緒
  • isExecuting - 當(dāng)前任務(wù)是否處于正在執(zhí)行
  • isFinished - 當(dāng)前任務(wù)是否完成
  • isCancelled - 當(dāng)前任務(wù)是否取消

狀態(tài)控制

  • 如果只重寫main方法酬核,底層控制變更任務(wù)執(zhí)行完成狀態(tài),以及任務(wù)退出适室。
  • 如果只重寫start方法嫡意,自行控制任務(wù)狀態(tài)

系統(tǒng)是怎樣移除一個(gè)isFinished=YES的NSOperation的?

系統(tǒng)是通過KVO監(jiān)聽實(shí)現(xiàn)的捣辆。

NSThread

啟動(dòng)流程

NSThread.png

源碼來自gnustep-base

- (void) start
{
  pthread_attr_t    attr;
  pthread_t     thr;

  if (_active == YES)
    {
      [NSException raise: NSInternalInconsistencyException
                  format: @"[%@-$@] called on active thread",
        NSStringFromClass([self class]),
        NSStringFromSelector(_cmd)];
    }
  if (_cancelled == YES)
    {
      [NSException raise: NSInternalInconsistencyException
                  format: @"[%@-$@] called on cancelled thread",
        NSStringFromClass([self class]),
        NSStringFromSelector(_cmd)];
    }
  if (_finished == YES)
    {
      [NSException raise: NSInternalInconsistencyException
                  format: @"[%@-$@] called on finished thread",
        NSStringFromClass([self class]),
        NSStringFromSelector(_cmd)];
    }

  /* Make sure the notification is posted BEFORE the new thread starts.
   */
  gnustep_base_thread_callback();

  /* The thread must persist until it finishes executing.
   */
  [self retain];

  /* Mark the thread as active whiul it's running.
   */
  _active = YES;

  errno = 0;
  pthread_attr_init(&attr);
  /* Create this thread detached, because we never use the return state from
   * threads.
   */
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
  /* Set the stack size when the thread is created.  Unlike the old setrlimit
   * code, this actually works.
   */
  if (_stackSize > 0)
    {
      pthread_attr_setstacksize(&attr, _stackSize);
    }
    //指定線程的氣筒函數(shù)為nsthreadLauncher
  if (pthread_create(&thr, &attr, nsthreadLauncher, self))
    {
      DESTROY(self);
      [NSException raise: NSInternalInconsistencyException
                  format: @"Unable to detach thread (last error %@)",
                  [NSError _last]];
    }
}
static void *nsthreadLauncher(void* thread)
{
    NSThread *t = (NSThread*)thread;//獲取啟動(dòng)線程t
    setThreadForCurrentThread(t);
#if __OBJC_GC__
    objc_registerThreadWithCollector();
#endif
#if GS_WITH_GC && defined(HAVE_GC_REGISTER_MY_THREAD)
  {
    struct GC_stack_base    base;

    if (GC_get_stack_base(&base) == GC_SUCCESS)
      {
    int result;

    result = GC_register_my_thread(&base);
    if (result != GC_SUCCESS && result != GC_DUPLICATE)
      {
        fprintf(stderr, "Argh ... no thread support in garbage collection library\n");
      }
      }
    else
      {
    fprintf(stderr, "Unable to determine stack base to register new thread for garbage collection\n");
      }
  }
#endif

  /*
   * Let observers know a new thread is starting.
   */
  if (nc == nil)
    {
      nc = RETAIN([NSNotificationCenter defaultCenter]);
    }
  //發(fā)送通知
  [nc postNotificationName: NSThreadDidStartNotification
            object: t
          userInfo: nil];

  [t main];

  [NSThread exit];
  // Not reached
  return NULL;
}
- (void) main
{
  if (_active == NO)
    {
      [NSException raise: NSInternalInconsistencyException
                  format: @"[%@-$@] called on inactive thread",
        NSStringFromClass([self class]),
        NSStringFromSelector(_cmd)];
    }

  [_target performSelector: _selector withObject: _arg];

}

多線程和鎖

iOS當(dāng)中都有哪些鎖蔬螟?

  • @synchronized
  • atomic
  • OSSpinLock
  • NSRecursiveLock
  • NSLock
  • dispatch_semaphore_t

@synchronized

  • 一般在創(chuàng)建單例對象的時(shí)候使用,保證多線程環(huán)境下創(chuàng)建對象是唯一的

@synchronized 的作用是創(chuàng)建一個(gè)互斥鎖汽畴,保證此時(shí)沒有其它線程對self對象進(jìn)行修改旧巾。這個(gè)是objective-c的一個(gè)鎖定令牌,防止self對象在同一時(shí)間內(nèi)被其它線程訪問忍些,起到線程的保護(hù)作用鲁猩。 一般在公用變量的時(shí)候使用,如單例模式或者操作類的static變量中使用罢坝。

指令@synchronized()需要一個(gè)參數(shù)廓握。該參數(shù)可以使任何的Objective-C對象,包括self嘁酿。這個(gè)對象就是互斥信號量隙券。他能夠讓一個(gè)線程對一段代碼進(jìn)行保護(hù),避免別的線程執(zhí)行該段代碼痹仙。

互斥鎖使用格式

@synchronized(鎖對象){ //需要鎖定的代碼 }

鎖定一份代碼只用1把鎖是尔,用多把鎖是無效的。

互斥鎖的優(yōu)缺點(diǎn)

優(yōu)點(diǎn):能有效防止因多線程搶奪資源造成的數(shù)據(jù)安全問題

缺點(diǎn):需要消耗大量的CPU資源

線程同步

多條線程在同一條線上執(zhí)行(按順序地執(zhí)行任務(wù))

互斥鎖开仰,就是使用了線程同步技術(shù)拟枚。

例如:一個(gè)電影院薪铜,有3個(gè)售票員。一場電影的總數(shù)量固定恩溅。3個(gè)售票員售票時(shí)隔箍,要判斷是非還有余票。

#import "ViewController.h"

@interface ViewController ()
/** 售票員01 */
@property (nonatomic, strong) NSThread *thread01;
/** 售票員02 */
@property (nonatomic, strong) NSThread *thread02;
/** 售票員03 */
@property (nonatomic, strong) NSThread *thread03;

/** 票的總數(shù) */
@property (nonatomic, assign) NSInteger ticketCount;

/** 鎖對象 */
//@property (nonatomic, strong) NSObject *locker;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
//    self.locker = [[NSObject alloc] init];
    
    self.ticketCount = 100;
    
    self.thread01 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
    self.thread01.name = @"售票員01";
    
    self.thread02 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
    self.thread02.name = @"售票員02";
    
    self.thread03 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
    self.thread03.name = @"售票員03";
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [self.thread01 start];
    [self.thread02 start];
    [self.thread03 start];
}

- (void)saleTicket
{
    while (1) {
        @synchronized(self) {
            // 先取出總數(shù)
            NSInteger count = self.ticketCount;
            if (count > 0) {
                self.ticketCount = count - 1;
                NSLog(@"%@賣了一張票脚乡,還剩下%zd張", [NSThread currentThread].name, self.ticketCount);
            } else {
                NSLog(@"票已經(jīng)賣完了");
                break;
            }
        }
    }
}

@end
atomic
  • 修飾屬性的關(guān)鍵字
  • 對被修飾對象進(jìn)行原子操作(不負(fù)責(zé)使用)
@property(atomic) NSMutableArray * array;

self.array = [NSMutableArray array];//?賦值是線程安全的

[self.array addObject:obj];//?操作數(shù)組是不能保證線程安全的
atomic和nonatomic的對比

1蜒滩、atomic和nonatomic用來決定編譯器生成的getter和setter是否為原子操作。

2奶稠、atomic:系統(tǒng)生成的 getter/setter 會(huì)保證 get俯艰、set 操作的完整性,不受其他線程影響锌订。getter 還是能得到一個(gè)完好無損的對象(可以保證數(shù)據(jù)的完整性)竹握,但這個(gè)對象在多線程的情況下是不能確定的,比如上面的例子辆飘。

也就是說:如果有多個(gè)線程同時(shí)調(diào)用setter的話啦辐,不會(huì)出現(xiàn)某一個(gè)線程執(zhí)行完setter全部語句之前,另一個(gè)線程開始執(zhí)行setter情況蜈项,相當(dāng)于函數(shù)頭尾加了鎖一樣芹关,每次只能有一個(gè)線程調(diào)用對象的setter方法,所以可以保證數(shù)據(jù)的完整性紧卒。

atomic所說的線程安全只是保證了getter和setter存取方法的線程安全侥衬,并不能保證整個(gè)對象是線程安全的。

3常侦、nonatomic:就沒有這個(gè)保證了浇冰,nonatomic返回你的對象可能就不是完整的value。因此聋亡,在多線程的環(huán)境下原子操作是非常必要的肘习,否則有可能會(huì)引起錯(cuò)誤的結(jié)果。但僅僅使用atomic并不會(huì)使得對象線程安全坡倔,我們還要為對象線程添加lock來確保線程的安全漂佩。

4、nonatomic的速度要比atomic的快罪塔。

5投蝉、atomic與nonatomic的本質(zhì)區(qū)別其實(shí)也就是在setter方法上的操作不同

atomic對象setter和getter方法的實(shí)現(xiàn):

- (void)setCurrentImage:(UIImage *)currentImage
{
    @synchronized(self) {
        if (_currentImage != currentImage) {
            [_currentImage release];
            _currentImage = [currentImage retain];
        }
    }
}

- (UIImage *)currentImage
{
    @synchronized(self) {
        return _currentImage;
    }
}

OSSpinLock - 自旋鎖

  • 循環(huán)等待詢問,不釋放當(dāng)前資源
  • 用于輕量級數(shù)據(jù)訪問征堪。比如簡單的int值+1/-1操作

runtime中有使用到瘩缆,進(jìn)行引用計(jì)數(shù)的+1/-1操作

NSLock

一般用于解決一些細(xì)粒度的線程同步問題,用來保證各個(gè)線程互斥來進(jìn)入自己的臨界區(qū)佃蚜。

//螞蟻金服
- (void)methodA {
  [nslock lock];
  [self methodB];
  [nslock unlock];
}
- (void)methodB {
  [nslock lock];
//操作邏輯
  [nslock unlock];
}

以上代碼的問題:死鎖

methodA中在某一線程的調(diào)用下庸娱,已經(jīng)對線程加了lock着绊,如果在methodB再次調(diào)用lock,由于鎖已經(jīng)被使用了且沒有解鎖,所以它需要等待鎖被解除熟尉,這樣就導(dǎo)致了死鎖归露,線程被阻塞住了。

NSRecursiveLock - 遞歸鎖

NSRecursiveLock實(shí)際上定義的是一個(gè)遞歸鎖斤儿,這個(gè)鎖可以被同一線程多次請求剧包,而不會(huì)引起死鎖。這主要是用在循環(huán)或遞歸操作中往果。

遞歸鎖會(huì)跟蹤它被lock的次數(shù)疆液。每次成功的lock都必須平衡調(diào)用unlock操作。只有所有達(dá)到這種平衡棚放,鎖最后才能被釋放枚粘,以供其它線程使用。

上面的問題可以通過使用遞歸鎖進(jìn)行解決:

- (void)methodA {
  [recursiveLock lock];
  [self methodB];
  [recursiveLock unlock];
}
- (void)methodB {
  [recursiveLock lock];
//操作邏輯
  [recursiveLock unlock];
}

dispatch_semaphore_t

dispatch_semaphore_create(long value); // 創(chuàng)建信號量
dispatch_semaphore_signal(dispatch_semaphore_t semaphore); // 發(fā)送信號量
dispatch_semaphore_wait(dispatch_semaphore_t semaphore, dispatch_time_t timeout); // 等待信號量
  • dispatch_semaphore_create(long value);和GCD的group等用法一致飘蚯,這個(gè)函數(shù)是創(chuàng)建一個(gè)dispatch_semaphore_類型的信號量,并且創(chuàng)建的時(shí)候需要指定信號量的大小福也。
  • dispatch_semaphore_signal(dispatch_semaphore_t semaphore);發(fā)送信號量局骤。該函數(shù)會(huì)對信號量的值進(jìn)行加1操作。
  • dispatch_semaphore_wait(dispatch_semaphore_t semaphore, dispatch_time_t timeout);等待信號量暴凑。如果信號量值為0峦甩,那么該函數(shù)就會(huì)一直等待,也就是不返回(相當(dāng)于阻塞當(dāng)前線程)现喳,直到該函數(shù)等待的信號量的值大于等于1凯傲,該函數(shù)會(huì)對信號量的值進(jìn)行減1操作,然后返回嗦篱。

dispatch_semaphore_create(long value);在底層會(huì)生成:

struct semaphore {
  int value; //信號量的值
  List <thread>; //一個(gè)線程列表
}

dispatch_semaphore_wait(dispatch_semaphore_t semaphore, dispatch_time_t timeout)在底層的實(shí)現(xiàn)邏輯大致如下:

dispatch_semaphore_wait() 
{
  S.value -= 1;
  if S.value < 0 then Block(S.list);//阻塞-主動(dòng)行為
}

dispatch_semaphore_signal()底層實(shí)現(xiàn)邏輯大致如下:

dispatch_semaphore_signal()
{
  S.value += 1;
  if S.value <= 0 then wakeup(S.list);//喚醒-被動(dòng)行為
}

通常等待信號量和發(fā)送信號量的函數(shù)是成對出現(xiàn)的冰单。并發(fā)執(zhí)行任務(wù)時(shí)候,在當(dāng)前任務(wù)執(zhí)行之前灸促,用dispatch_semaphore_wait函數(shù)進(jìn)行等待(阻塞)诫欠,直到上一個(gè)任務(wù)執(zhí)行完畢后且通過dispatch_semaphore_signal函數(shù)發(fā)送信號量(使信號量的值加1),dispatch_semaphore_wait函數(shù)收到信號量之后判斷信號量的值大于等于1浴栽,會(huì)再對信號量的值減1荒叼,然后當(dāng)前任務(wù)可以執(zhí)行,執(zhí)行完畢當(dāng)前任務(wù)后典鸡,再通過dispatch_semaphore_signal函數(shù)發(fā)送信號量(使信號量的值加1)被廓,通知執(zhí)行下一個(gè)任務(wù)......如此一來,通過信號量萝玷,就達(dá)到了并發(fā)隊(duì)列中的任務(wù)同步執(zhí)行的要求嫁乘。

總結(jié)

  • 怎樣用GCD事項(xiàng)多讀單寫英遭?

  • iOS系統(tǒng)為我們提供的幾種多線程技術(shù)各自的特點(diǎn)是怎樣的?

在iOS系統(tǒng)當(dāng)中主要提供了三種多線程技術(shù)亦渗,分別為GCD, NSOperation, NSThread挖诸,一般使用GCD來解決一些簡單的線程同步法精,包括一些子線程的分派搂蜓,包括實(shí)現(xiàn)一些例如多讀單寫這種場景的問題的解決帮碰。對于NSOperation殉挽,比如AFNetworking斯碌,SDWebimageView傻唾,他們內(nèi)部都會(huì)涉及到NSOperation,由于他的特點(diǎn)是可以方便我們對任務(wù)的狀態(tài)進(jìn)行控制冠骄,包括可以控制依賴的添加和移除依賴凛辣。對于NSThread往往我們用他來實(shí)現(xiàn)同一個(gè)常駐線程。

  • NSOperation對象在Finished之后是怎樣從隊(duì)列中移除掉的蝙砌?

NSOperation對象在Finished之后,會(huì)在內(nèi)部用KVO的形式通知NSOperationQueue達(dá)到對NSOperation的移除

你都用過哪些鎖肚邢?結(jié)合實(shí)際談?wù)勀闶窃鯓邮褂玫模?/p>

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末骡湖,一起剝皮案震驚了整個(gè)濱河市响蕴,隨后出現(xiàn)的幾起案子浦夷,更是在濱河造成了極大的恐慌罐孝,老刑警劉巖莲兢,帶你破解...
    沈念sama閱讀 216,744評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件改艇,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)瘦穆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評論 3 392
  • 文/潘曉璐 我一進(jìn)店門扛或,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人艾恼,你說我怎么就攤上這事钠绍。” “怎么了碱屁?”我有些...
    開封第一講書人閱讀 163,105評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長柿赊。 經(jīng)常有香客問我闹瞧,道長奥邮,這世上最難降的妖魔是什么洽腺? 我笑而不...
    開封第一講書人閱讀 58,242評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮藕坯,結(jié)果婚禮上炼彪,老公的妹妹穿的比我還像新娘辐马。我一直安慰自己喜爷,他們只是感情好檩帐,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,269評論 6 389
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著勾缭,像睡著了一般毒嫡。 火紅的嫁衣襯著肌膚如雪兜畸。 梳的紋絲不亂的頭發(fā)上咬摇,一...
    開封第一講書人閱讀 51,215評論 1 299
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼芒珠。 笑死,一個(gè)胖子當(dāng)著我的面吹牛好爬,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,096評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼有额!你這毒婦竟也來了茴迁?” 一聲冷哼從身側(cè)響起堕义,我...
    開封第一講書人閱讀 38,939評論 0 274
  • 序言:老撾萬榮一對情侶失蹤椿争,失蹤者是張志新(化名)和其女友劉穎褐捻,沒想到半個(gè)月后舍扰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,354評論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,573評論 2 333
  • 正文 我和宋清朗相戀三年获洲,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了最爬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,745評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡帮坚,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情溉箕,我是刑警寧澤,帶...
    沈念sama閱讀 35,448評論 5 344
  • 正文 年R本政府宣布棋凳,位于F島的核電站,受9級特大地震影響晓铆,放射性物質(zhì)發(fā)生泄漏蠢箩。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,048評論 3 327
  • 文/蒙蒙 一呵萨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧跨跨,春花似錦潮峦、人聲如沸囱皿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽嘱腥。三九已至,卻和暖如春拘悦,著一層夾襖步出監(jiān)牢的瞬間齿兔,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評論 1 269
  • 我被黑心中介騙來泰國打工础米, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留分苇,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,776評論 2 369
  • 正文 我出身青樓屁桑,卻偏偏與公主長得像医寿,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子蘑斧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,652評論 2 354

推薦閱讀更多精彩內(nèi)容

  • iOS多線程編程 基本知識 1. 進(jìn)程(process) 進(jìn)程是指在系統(tǒng)中正在運(yùn)行的一個(gè)應(yīng)用程序靖秩,就是一段程序的執(zhí)...
    陵無山閱讀 6,043評論 1 14
  • 多線程的四種解決方案:pthread,NSThread竖瘾,GCD沟突,NSOperation 一、多線程的基本概念進(jìn)程:...
    陽明先生_X自主閱讀 469評論 0 3
  • 進(jìn)程與線程 進(jìn)程:計(jì)算機(jī)操作系統(tǒng)分配資源的單位捕传,是指系統(tǒng)中正在運(yùn)行的應(yīng)用程序惠拭,進(jìn)程之間相互獨(dú)立,運(yùn)行在受保護(hù)的內(nèi)存...
    三十六_閱讀 377評論 1 1
  • 一乐横、前言 上一篇文章iOS多線程淺匯-原理篇中整理了一些有關(guān)多線程的基本概念求橄。本篇博文介紹的是iOS中常用的幾個(gè)多...
    nuclear閱讀 2,050評論 6 18
  • 本文轉(zhuǎn)載自:行走的少年郎的簡書:iOS多線程:『GCD』詳盡總結(jié) 本文用來介紹 iOS 多線程中 GCD 的相關(guān)知...
    遠(yuǎn)遊旳遊子閱讀 1,114評論 0 10