iOS多线程编程

一、进程

1.什么是进程?
  • 进程是指在系统中正在运行的一个应用程序
  • 每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内

二、线程

1.什么是线程?
  • 应用程序中一条任务的执行路径
2.进程和线程是什么关系?
  • 一个进程可以包含多个线程(一个进程至少包含一个线程Main线程)
  • 一个进程中的所有任务都是在线程中执行
3.线程中的任务怎么执行?
  • 如果要在一个线程中执行多个任务,那么只能一个一个地按顺序执行这些任务,在同一时间内,一个线程只能执行一个任务
  • 比如在一个线程中下载三个文件A、B、C,那么会先下载文件A,然后下载文件B,再下载文件C
4.什么是多线程
  • 一个进程中可以开启多条线程,每条线程可以并行(同时)执行不同的任务
  • 比如在一个线程中下载三个文件A、B、C,开启三条线程,每个线程下载一个文件,那么三个文件会同时去下载
5.多线程实现原理
  • .同一时间,CPU只能处理1条线程,只有1条线程在工作(执行)
  • .多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换)
  • 如果CPU调度线程的时间足够快,就造成了多线程并发执行
6.多线程的优缺点
  • 优点一:多线程技术可以提高程序的执行效率
  • 优点二:能适当提高资源利用率(CPU、内存利用率)
  • 缺点一:大量使用多线程CPU会在N多线程之间调度,消耗大量的CPU资源,每条线程被调度执行的频次会降低(线程的执行效率降低)
  • 缺点二:创建线程是有开销的,iOS下主要成本包括:内核数据结构(大约1KB)、栈空间(子线程512KB、主线程1MB,也可以使用-setStackSize:设置,但必须是4K的倍数,而且最小是16K),创建线程大约需要90毫秒的创建时间

三、iOS中多线程实现方案

iOS中多线程实现方案

四、pthread

1.C语言API,使用比较麻烦
//开启新的线程执行run方法
- (void)pthread {
    pthread_t thread;
    pthread_create(&thread, NULL, run, NULL);
}

void * run(void *param){
    for (NSInteger i = 0; i<50000; i++) {
        NSLog(@"---------%zd", i);
    }
    return NULL;
}

五、NSThread

1.一个NSThread对象就代表一个线程,以下是几种创建方式
  • 第一种多线程方式,需要调用start方法才能启动
- (void)createThread1{
    // 创建线程,线程对象(局部变量)系统会自己加持,在任务执行完之前不会被销毁
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"jack"];
    thread.name = @"my-thread";
    // 启动线程
    [thread start];
}

- (void)run:(NSString *)param{
    for (NSInteger i = 0; i<100; i++) {
        NSLog(@"------%@", [NSThread currentThread]);
    }
}

  • 第二种多线程方式,创建线程后自动启动线程
- (void)createThread2{
    [NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"rose"];
}
- (void)run:(NSString *)param{
    for (NSInteger i = 0; i<100; i++) {
        NSLog(@"------%@", [NSThread currentThread]);
    }
}
  • 第三种多线程方式,隐式创建并启动线程
- (void)createThread3{
    [self performSelectorInBackground:@selector(run:) withObject:@"jack"];
}
- (void)run:(NSString *)param{
    for (NSInteger i = 0; i<100; i++) {
        NSLog(@"------%@", [NSThread currentThread]);
    }
}
2.NSThread的线程通信
  • 在一个线程中执行完特定任务后,转到另一个线程继续执行任务,一个线程传递数据给另一个线程,API如下:
- (void)performSelectorOnMainThread:(SEL)aSelector 
                         withObject:(id)arg 
                      waitUntilDone:(BOOL)wait;

- (void)performSelector:(SEL)aSelector
               onThread:(NSThread *)thread
             withObject:(id)arg   
          waitUntilDone:(BOOL)wait;

  • 线程通信示例:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [self performSelectorInBackground:@selector(download3) withObject:nil];
}

//子线程下载图片数据
- (void)download3{
    NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
    NSData *data = [NSData dataWithContentsOfURL:url];
    UIImage *image = [UIImage imageWithData:data];
    
    // 回到主线程,显示图片
    [self.imageView performSelector:@selector(setImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:NO];
    //[self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO];
    //[self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:YES];

    NSLog(@" --- waitUntilDone ---");
    //waitUntilDone参数:
    //YES:会等到setImage方法走完以后打印最后一句log。
    //NO:在执行setImage方法的时候不影响最后一句log的打印,两个是同时进行
}

//主线程显示图片
- (void)showImage:(UIImage *)image{
    self.imageView.image = image;
}

3.NSThread常用的方法
//获得当前线程
NSThread *current = [NSThread currentThread];

+ (NSThread *)mainThread; // 获得主线程
- (BOOL)isMainThread; // 是否为主线程
+ (BOOL)isMainThread; // 是否为主线程

//线程的名字,适用于第一种方式创建的线程(创建的时候返回NSThread的对象)
- (void)setName:(NSString *)n;
- (NSString *)name;

六、GCD

1.什么是GCD
  • 全称是Grand Central Dispatch,可译为“重要的中枢调度器”
  • GCD是苹果公司为多核的并行运算提出的解决方案
  • GCD会自动利用更多的CPU内核(比如双核、四核)
  • GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
2.任务:执行什么操作
  • 用同步的方式执行任务:
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
  • 用异步的方式执行任务
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
  • 同步和异步的区别

同步:只能在当前线程中执行任务,不具备开启新线程的能力
异步:可以在新的线程中执行任务,具备开启新线程的能力

3.队列
  • 并发队列(Concurrent Dispatch Queue)

可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)
并发功能只有在异步(dispatch_async)函数下才有效

  • 串行队列(Serial Dispatch Queue)

让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)

  • 获取并发队列
GCD默认已经提供了全局的并发队列,供整个应用使用,不需要手动创建
使用dispatch_get_global_queue函数获得全局的并发队列
dispatch_queue_t dispatch_get_global_queue(
dispatch_queue_priority_t priority, // 队列的优先级
unsigned long flags); // 此参数暂时无用,用0即可
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 获得全局并发队列

全局并发队列的优先级
#define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默认(中)
#define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MI

//自己创建一个并发队列
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
  • 获取串行队列
GCD中获得串行有2种途径
使用dispatch_queue_create函数创建串行队列
dispatch_queue_t
dispatch_queue_create(const char *label, // 队列名称 
dispatch_queue_attr_t attr); // 队列属性,一般用NULL即可,DISPATCH_QUEUE_SERIAL串行就传NULL
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL); // 创建
dispatch_release(queue); // 非ARC需要释放手动创建的队列

使用主队列(跟主线程相关联的队列)
主队列是GCD自带的一种特殊的串行队列
放在主队列中的任务,都会放到主线程中执行
使用dispatch_get_main_queue()获得主队列
dispatch_queue_t queue = dispatch_get_main_queue();
  • 各种队列和发送方法的组合
// 同步函数 + 主队列:(若是在主线程调用会卡死主线程)
- (void)syncMain{
    NSLog(@"syncMain ----- begin");
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_sync(queue, ^{
        NSLog(@"1-----%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2-----%@", [NSThread currentThread]);
    });
    NSLog(@"syncMain ----- end");
}

// 异步函数 + 主队列:只在主线程中执行任务
- (void)asyncMain{
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_async(queue, ^{
        NSLog(@"1-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2-----%@", [NSThread currentThread]);
    });
}


 //同步函数 + 串行队列:不会开启新的线程,在当前线程执行任务。任务是串行的,执行完一个任务,再执行下一个任务
- (void)syncSerial{
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(queue, ^{
        NSLog(@"1-----%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2-----%@", [NSThread currentThread]);
    });
}

 //异步函数 + 串行队列:会开启新的线程,但是任务是串行的,执行完一个任务,再执行下一个任务
- (void)asyncSerial{
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        NSLog(@"1-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2-----%@", [NSThread currentThread]);
    });
}


 //同步函数 + 并发队列:不会开启新的线程
- (void)syncConcurrent{
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_sync(queue, ^{
        NSLog(@"1-----%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2-----%@", [NSThread currentThread]);
    });
    //会先执行任务再执行这句打印,sync的任务会立刻执行
    NSLog(@"syncConcurrent--------end");
}


// 异步函数 + 并发队列:可以同时开启多条线程
- (void)asyncConcurrent{
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i<10; i++) {
            NSLog(@"1-----%@", [NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i<10; i++) {
            NSLog(@"2-----%@", [NSThread currentThread]);
        }
    });
    //会先执行这一句打印,再执行添加到队列里面的任务
    NSLog(@"asyncConcurrent--------end");
}
  • 各种组合图示


    各种组合图示
  • GCD中的实用方法
//1.延迟执行
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
});

//2.一次性代码:使用dispatch_once函数能保证某段代码在程序运行过程中只被执行1次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    // 只执行1次的代码(这里面默认是线程安全的)
});

//3.队列组
//分别异步执行2个耗时的操作,等2个异步操作都执行完毕后,再回到主线程执行操作
dispatch_group_t group =  dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // 执行1个耗时的异步操作
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // 执行1个耗时的异步操作
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    // 等前面的异步操作都执行完毕后,回到主线程...
});

//4.快速迭代
dispatch_apply(10, queue, ^(size_t index) {
     //queue如果是串行队列index是有顺序的
     //queue如果是并发队列index是无序的(并发执行)
});

//5.barrier函数:保证barrier之前的任务是并发执行,然后执行barrier任务,然后执行barrier之后的任务
- (void)barrier{
    dispatch_queue_t queue = dispatch_queue_create("12312312", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        NSLog(@"----1-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"----2-----%@", [NSThread currentThread]);
    });
    
    dispatch_barrier_async(queue, ^{
        NSLog(@"----barrier-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"----3-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"----4-----%@", [NSThread currentThread]);
    });
}

七、NSOperation

1.NSOperation和NSOperationQueue实现多线程的具体步骤
  • NSOperation和NSOperationQueue是对GCD的一层封装
  • NSOperation和NSOperationQueue实现多线程的具体步骤
  • 先将需要执行的操作封装到一个NSOperation对象中
  • 然后将NSOperation对象添加到NSOperationQueue中
  • 系统会自动将NSOperationQueue中的NSOperation取出来
  • 将取出的NSOperation封装的操作放到一条新线程中执行
2.NSOperation:是个抽象类,并不具备封装操作的能力,必须使用它的子类
  • NSInvocationOperation
//默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作
//只有将NSOperation放到一个NSOperationQueue中,才会异步执行操作
- (void)invocationOperation{
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
    [op start];
}

- (void)run{
    NSLog(@"------%@", [NSThread currentThread]);
}
  • NSBlockOperation
//只要NSBlockOperation封装的操作数 > 1,就会异步执行操作
- (void)blockOperation{
    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        // 在主线程
        NSLog(@"下载1------%@", [NSThread currentThread]);
    }];
    
    // 添加额外的任务(在子线程执行)
    [op addExecutionBlock:^{
        NSLog(@"下载2------%@", [NSThread currentThread]);
    }];
    
    [op start];
}
3.NSOperationQueue
  • NSOperation可以调用start方法来执行任务,但默认是同步执行的
  • 如果将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作
  • 添加操作到NSOperationQueue中
- (void)addOperation:(NSOperation *)op;//添加op的时候会默认调用[op start];
- (void)addOperationWithBlock:(void (^)(void))block;//会默认调用[op start];
4.NSOperation和NSOperationQueue的组合使用
  • NSInvocationOperation和NSOperationQueue
- (void)operationQueue1{
    // 创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    // 创建NSInvocationOperation
    NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download1) object:nil];
    NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download2) object:nil];
    
    // 添加任务到队列中
    [queue addOperation:op1]; // [op1 start]
    [queue addOperation:op2]; // [op2 start]
}

- (void)download1{
    NSLog(@"download1 --- %@", [NSThread currentThread]);
}

- (void)download2{
    NSLog(@"download2 --- %@", [NSThread currentThread]);
}
  • NSBlockOperation和NSOperationQueue的组合使用
- (void)operationQueue1{
    // 创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    // 创建NSBlockOperation
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download3 --- %@", [NSThread currentThread]);
    }];
    
    [op3 addExecutionBlock:^{
        NSLog(@"download4 --- %@", [NSThread currentThread]);
    }];
    [op3 addExecutionBlock:^{
        NSLog(@"download5 --- %@", [NSThread currentThread]);
    }];
    
    NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download6 --- %@", [NSThread currentThread]);
    }];
    
    // 添加任务到队列中
    [queue addOperation:op3]; // [op3 start]
    [queue addOperation:op4]; // [op4 start]
}
5.如果控制NSOperationQueue是串行队列还是并发队列
  • 默认最大并发数是-1,也就是不限制最大并发数,当有多个任务的时候会开启多条线程同时执行任务
  • maxConcurrentOperationCount:同时执行的任务个数
- (void)maxConcurrentOperationCountTest {
    
    // 创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
   // 变成了串行队列,会开启一条线程,任务依次执行
    queue.maxConcurrentOperationCount = 1; 
    
    // 添加操作
    [queue addOperationWithBlock:^{
        NSLog(@"download1 --- %@", [NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"download2 --- %@", [NSThread currentThread]);
    }];
}
6.队列的取消、暂停、恢复
  • 取消队列的所有操作
- (void)cancelAllOperations;
//也可以调用NSOperation的- (void)cancel方法取消单个操作
  • 暂停和恢复队列
- (void)setSuspended:(BOOL)b; // YES代表暂停队列,NO代表恢复队列
- (BOOL)isSuspended;
7.操作的相互依赖:NSOperation之间可以设置依赖来保证执行顺序
  • 比如一定要让操作A执行完后,才能执行操作B,可以这么写
[operationB addDependency:operationA]; // 操作B依赖于操作A
  • 任务可以夸队列依赖,在不同队列里面的任务也可以相互依赖


    任务的跨队列依赖
  • 任务不可以相互依赖,会卡死:

[operationB addDependency:operationA]; // 操作B依赖于操作A
[operationA addDependency:operationB]; // 操作A依赖于操作B

八、线程状态

1.线程状态
  • 创建一个线程调用了start方法以后线程对象会放入可调度线程池,进入就绪状态,等待CPU的调度
  • 当CPU调度的时候处于runing状态,当CPU调用其他线程的时候当前线程会进入就绪状态
  • 调用了sleep方法\等待同步锁会进入阻塞状态
  • sleep到时\得到同步锁进入就绪状态
  • 线程任务执行完毕、异常\强制退出,进入死亡状态
2.线程状态示意图:
线程状态示意图
3.线程状态相关方法
启动线程
- (void)start; 
// 进入就绪状态 -> 运行状态。当线程任务执行完毕,自动进入死亡状态

阻塞(暂停)线程
+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
// 进入阻塞状态

强制停止线程
+ (void)exit;
// 进入死亡状态

注意:一旦线程停止(死亡)了,就不能再次开启任务

九、多线程资源抢夺

1.资源抢夺简述:
  • 一块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源,比如多个线程访问同一个对象、同一个变量、同一个文件
  • 当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题
2.资源抢夺示意图:
资源抢夺示意图
3.线程同步(互斥锁)
  • 线程同步:多条线程在同一条线上执行(按顺序地执行任务),互斥锁,就是使用了线程同步技术.
  • 互斥锁对某段代码进行加锁
  • 一个线程在进入之前会判断锁的状态是否是打开,若果是打开则加锁,然后进入代码进行操作,操作完成后解锁。在操作过程中如果有其他线程想对这段代码进行操作则需要等待锁放开才能进入操作
  • 互斥锁虽然能有效解决多线程的资源抢夺问题,同时需要消耗大量的CPU资源
//注意:锁定1份代码只用1把锁,用多把锁是无效的(一般self即可)
@synchronized(锁对象) {
 // 需要锁定的代码  
}
4.互斥锁的原理
互斥锁的原理
5.原子和非原子属性
  • OC在定义属性时有nonatomic和atomic两种选择
  • atomic:原子属性,为setter方法加锁,是线程安全的(默认就是atomic)
  • nonatomic:非原子属性,不会为setter方法加锁

十、多线程下载多图片示例

多线程下载多图片demo 密码:94ts

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容

  • 目录 一、多线程简介 1、多线程的由来2、耗时操作的模拟试验3、进程和线程4、多线程的概念及原理5、多线程的优缺点...
    宁梓茞阅读 461评论 0 0
  • iOS多线程编程 基本知识 1. 进程(process) 进程是指在系统中正在运行的一个应用程序,就是一段程序的执...
    陵无山阅读 5,905评论 1 14
  • 一.概述 1.基本概念 同步与异步的概念 同步 必须等待当前语句执行完毕,才可以执行下一个语句。 异步 不用等待当...
    Jt_Self阅读 455评论 0 1
  • 概览 进程与线程的概念 多线程的由来 并行与并发 多线程的实现 串行与并行 线程的几种状态 串行队列与并发队列区别...
    a_只羊阅读 216评论 0 0
  • 文/雪诺 微信公众号:迷茫人生路 总是喜欢在自己迷茫无助的时候去寻求一丝慰藉,失败时的倾诉、伤心时的哭...
    Snow凤阅读 104评论 0 0