一:基本概念

1:进程:正在运行的程序为进程。

2:线程:每个进程要想执行任务必须得有线程,进程中任务的执行都是在线程中。

3:线程的串行:一条线程里任务的执行都是串行的,假如有一个进程开辟了一条线程,此条线程里有ABC三个任务,则ABC三个任务的执行是串行的,ABC三个任务按顺序一个个执行

4:多线程:一个进程可以开辟多条线程去并行(同时)去执行任务,假如有ABC三个任务,则此进程会开辟多条线程去并发执行ABC三个任务,可以提高运行效率

5:线程的原理:同一时间,cpu只能处理一条线程,而且也只有一条线程在工作,多线程的并发执行,也就是cpu在多条线程间爱会切换调度。进程一启动,就默认创建了一条线程为主线程大约为1M,所创建的子线程大约为512KB。

6:多线程的缺点:1:大量开线程,会大量占用cpu资源,cpu在调度线程上的开销也就越大 2:大量开线程,会使每条线程的执行效率降低,程序的性能降低,对于移动开发来说,适当开辟线程,大量开线程会使程序的想能降低

7:ios开发中多线程的应用:程序一启动就默认会开辟一条线程,成为主线程,也就是UI线程,主线程主要负责,刷新和显示UI,处理按钮的点击事件,表格的滚动拖拽事件,尽量避免把耗时的操作放在主线程。凡在主线程执行的任务都是串行的,按照顺序一个个执行,若把耗时的操作放在主线程,则会卡主主线程,影响主线程的其他操作,造成很差的用户体验。正确的做法是:将比较耗时的操作放在子线程中去执行,不要影响主线程的其他操作。

8:获得当前线程:[NSThread currentThread]; 获得主线程:[NSThread mainThread];

9:ios中的多线程方案:1:Pthread:跨平台,适用于各个系统,线程的生命周期由程序员去管理。 2:NSThread 3:GCD:由系统自动去管理线程的声明周期   4:NSOperation:基于GCD封装,也是右系统去管理线程的生命周期。

二:线程的安全:

1:线程安全:当同一块资源被多条线程同时访问的时候,会涉及到线程安全的问题,例如卖票,银行取钱问题。解决方法:加互斥锁

@synchronized(self) {被锁住的代码 }(加锁是很耗性能的,当只有多条线程访问统一资源的时候,才考虑去加互斥锁)。加锁的工作原理是:加互斥锁后,同一时间只许可一条线程访问该资源,其他线程无法访问。当某条线程访问该资源时,会加一把锁,被锁住的代码执行完毕后,锁会被打开,下个线程此时才可以访问该资源,注意:锁对象必须是唯一的,否则多条线程依然可以访问该资源。锁对象一般用self,因为在控制器中,控制器对象就是唯一的,保证了锁对象的唯一性。

2:线程同步:加锁用的就是线程同步技术,多条线程同一条线程上按顺序依次执行 线程异步:多条线程异步去执行

3:原子与非原子属性:atomic是原子属性,默认是线程安全的,在执行setter方法赋值的时候,会加一把互斥锁,等待赋值结束后,所才被打开,防止了多条线程同时访问造成的线程安全问题。虽然是线程安全的,但是加锁消耗了大量的资源 。nonatomic:非原子属性,线程不安全的,没有加锁,因为在实际移动开发中,调用setter方法大部分都是在主线程,所以不涉及多条线程同时访问的问题。若涉及了多线程同时访问该资源,可以加一把互斥锁,还可以用atomic原子属性去修饰

三:GDC

1:GCD有两个核心的概念:1:队列 2:任务,其中队列是用来存放任务,GCD的使用:创建任务直接将任务放到队列里,GCD会自动将任务从队列里取出,按照FIFO原则(先进先出,后进后出,栈是现进后出)取出任务,放到相应的线程中去执行任务。

2:GCD有两个常用的函数用来执行任务:1:同步函数: dispatch_sync

dispatch_sync(queue, ^{

});

2:异步函数:dispatch_async

dispatch_async(queue, ^{

});

其中queue为队列,block为任务。其中同步函数:在当前线程中按顺序串行执行任务,不具备开启线程的能力 异步函数:在新的线程中执行,具备开启新线程的能力

3:GCD两大队列:1:串行队列:队列中的任务按顺序执行,一个任务执行完毕才会去执行下一个任务  2:并发队列:可以并发同时执行多个任务,并发队列只有在异步函数 dispatch_async中才能起到并发执行的作用

总结:1:同步和异步主要影响能不能开启新的线程 2:串行队列和并发队列主要影响任务执行的方式:串行:按顺序依次执行,并发队列:多个任务同时执行 3:看一条线程,先看最外层是同步还是异步,若是同步,不能开启新的线程,只在当前的线程中执行,若是异步,则具备开启新线程的能力,可以在新县城中执行 再看内层:若是串行队列:则任务按顺序执行 若是并发队列:则可以并发同时还行任务,但只有在异步函数中才会并发执行任务。

4:队列的创建:

1:dispatch_queue_t queue = dispatch_queue_create(a,b);a参数为队列名称,C语言字符串,用"",b参数为队列,串行队列:DISPATCH_QUEUE_SERIAL,也可以这么创建串行队列

dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", NULL);

并发队列:DISPATCH_QUEUE_SERIAL NULL  注意:1:异步并发队列有三个任务,则并不会一定开启三条线程,开启线程的个数是由GCD内部去决定的。2:任务添加到队列后,会按FIFO原则将任务取出,并发执行,也就是三个任务的执行先后顺序是不确定的

2:并发队列的创建:1:可以获取到全局并发队列:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);第一个参数为队列优先级,默认为DEFAULT,也可以传0,第二个参数暂时用不到传0就可以,当涉及多条线程需要设定优先级时,可以设置第一个参数 2:也可以手动去创建并发队列dispatch_queue_create(a,b)

5:GCD的各种队列:

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
} - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self syncMain];
} /**
* 同步函数 + 主队列:只打印begin,是因为同步函数放在串行队列中会卡主当前线程,造成死锁。同步函数不开启线程在当前线程中执行,且立即执行,而主线程的任务end要先执行完再去执行同步函数中任务,而同步函数又要立即执行,所以会造成死锁
*/
- (void)syncMain
{
NSLog(@"syncMain ----- begin"); // 1.获得主队列
dispatch_queue_t queue = dispatch_get_main_queue(); // 2.将任务加入队列
dispatch_sync(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
}); NSLog(@"syncMain ----- end");
} /**
* 异步函数 + 主队列:只在主线程中执行任务。无论同步还是异步,只要将任务放到追队列里,任务就会在主线程中执行,且是串行执行任务,主队列是一种特殊的串行队列。因为123任务是后添加到主队列里的任务,所以先执行完 start 和 end 在执行 1,2,3
*/
- (void)asyncMain
{ NSLog(@"syncConcurrent--------start");
// 1.获得主队列
dispatch_queue_t queue = dispatch_get_main_queue(); // 2.将任务加入队列
dispatch_async(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
NSLog(@"syncConcurrent--------end");
} /**
* 同步函数 + 串行队列:同步不会开启新的线程,在当前线程串行执行任务,且立即执行任务。任务是串行的,执行完一个任务,再执行下一个任务。打印顺序:start 1,2,3 end
*/
- (void)syncSerial
{ NSLog(@"syncConcurrent--------start");
// 1.创建串行队列
dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_SERIAL); // 2.将任务加入队列
dispatch_sync(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
}); NSLog(@"syncConcurrent--------end");
} /**
* 异步函数 + 串行队列:异步函数会开启新的线程,但是任务是串行的,任务按顺序执行执行完一个任务,再执行下一个任务。所以只开启一条线程(在一条线程中任务是按照顺序一个接一个执行)。打印顺序:statrt end 1 2 3。相当于把asyncSerial任务放到了主队列中执行,任务则在主线程中执行,又将任务123放到了串行队列中执行,所以先执行完主线程的任务,在执行串行队列的任务,所以先打印start end 再按照顺序依次执行任务1,2,3
*/
- (void)asyncSerial { NSLog(@"syncConcurrent--------start");
// 1.创建串行队列
dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_SERIAL);
// dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", NULL); // 2.将任务加入队列
dispatch_async(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
}); NSLog(@"syncConcurrent--------end");
} /**
* 同步函数 + 并发队列:不会开启新的线程,在当前线程中串行执行任务且立即执行任务,因为并发队列只有在异步函数中才会起作用,打印顺序:start 1,2,3,end
*/
- (void)syncConcurrent
{
NSLog(@"syncConcurrent--------start");
// 1.获得全局的并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ); // 2.将任务加入队列
dispatch_sync(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
}); NSLog(@"syncConcurrent--------end");
} /**
* 异步函数 + 并发队列:1:可以同时开启多条线程并发同时执行任务,开启线程的个数不确定,并且并发队列只有在异步函数下才有效 2:代码执行的顺序是先打印 satrt 在打印 end 最后再打印子线程中的内容,先执行主线程中的任务,在执行异步线程的任务,123任务执行完成的先后顺序是不确定的。原因是:相当于把asyncConcurrent任务放到了主队列里,在主线程执行,任务是按照FIFO原则从队列中取出,所以先执行完start后,分别将任务123添加到了全局并发队列里,123任务是后添加到队列中的,所以先执行完主队列任务,再异步并发执行子线程中的任务。
*/
- (void)asyncConcurrent
{
// 1.创建一个并发队列
// label : 相当于队列的名字
// dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_CONCURRENT); NSLog(@"asyncConcurrent--------start"); // 1.获得全局的并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ); // 2.将任务加入队列
dispatch_async(queue, ^{
for (NSInteger i = ; i<; i++) {
NSLog(@"1-----%@", [NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (NSInteger i = ; i<; i++) {
NSLog(@"2-----%@", [NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (NSInteger i = ; i<; i++) {
NSLog(@"3-----%@", [NSThread currentThread]);
}
}); NSLog(@"asyncConcurrent--------end"); } @end

6:GCD中的线程间通信

#import "ViewController.h"

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView; @end @implementation ViewController - (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
} - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { NSLog(@"---------------------------------------1");
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
NSLog(@"---------------------------------------3");
// 图片的网络路径
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]; // 回到主线程
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
NSLog(@"-------------------------------------5");
}); NSLog(@"---------------------------------------4"); }); NSLog(@"---------------------------------------2");
} @end

打印顺序:1,2,3,4,5,先执行主线程中的任务,1,2 主线程中的任务执行完毕后再去异步执行子线程中的任务,先执行完子线程中的任务3,4 后,再回到主线程执行任务5。原因:相当于把touchBegan任务放到了主队列里,任务在主线程中执行,子线程中的任务是后面添加到并发队列里的,所以先回执行完主线程中的任务1,2 当执行完毕后再去执行子线程中的任务,在子线程中的任务5是后面加入到主队列的,所以先回执行完子线程中的任务3,4,再回到主线程里执行任务5

6:GCD的其他函数

#import "ViewController.h"
#import "XMGPerson.h" @interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
/** 图片1 */
@property (nonatomic, strong) UIImage *image1;
/** 图片2 */
@property (nonatomic, strong) UIImage *image2;
@end @implementation ViewController - (void)viewDidLoad {
[super viewDidLoad]; XMGPerson *person1 = [[XMGPerson alloc] init];
XMGPerson *person2 = [[XMGPerson alloc] init];
XMGPerson *person3 = [[XMGPerson alloc] init];
XMGPerson *person4 = [[XMGPerson alloc] init]; // XMGPerson *p1 = [[XMGPerson alloc] init];
// NSLog(@"%@", p1.books);
//
// XMGPerson *p2 = [[XMGPerson alloc] init];
// NSLog(@"%@", p2.books); } void download(void * data)
{ } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
// dispatch_async(<#dispatch_queue_t queue#>, <#^(void)block#>);
// dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// dispatch_async_f(queue, NULL, download); // [self group];
[self barrier];
}
/**
* 群组:1:群组中的任务是并发执行的,会等群组中的任务都执行完毕后,会调用 dispatch_group_notify,在并发队列中完成绘图操作,在主线程中去显示图片。1:首先创建群组: dispatch_group_t group = dispatch_group_create(); 2:将任务放到并发队列中,将队列放到群组中,12任务执行完不确定 3:都执行完调用dispatch_group_async,将耗时的绘图操作还放在异步线程,绘图完毕后,回到主线程显示UI
*/
- (void)group
{ dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, );
// 创建一个队列组
dispatch_group_t group = dispatch_group_create(); // 1.下载图片1
dispatch_group_async(group, queue, ^{
// 图片的网络路径
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]; // 生成图片
self.image1 = [UIImage imageWithData:data];
}); // 2.下载图片2
dispatch_group_async(group, queue, ^{
// 图片的网络路径
NSURL *url = [NSURL URLWithString:@"http://pic38.nipic.com/20140228/5571398_215900721128_2.jpg"]; // 加载图片
NSData *data = [NSData dataWithContentsOfURL:url]; // 生成图片
self.image2 = [UIImage imageWithData:data];
}); // 3.将图片1、图片2合成一张新的图片
dispatch_group_notify(group, queue, ^{
// 开启新的图形上下文
UIGraphicsBeginImageContext(CGSizeMake(, )); // 绘制图片
[self.image1 drawInRect:CGRectMake(, , , )];
[self.image2 drawInRect:CGRectMake(, , , )]; // 取得上下文中的图片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); // 结束上下文
UIGraphicsEndImageContext(); // 回到主线程显示图片
dispatch_async(dispatch_get_main_queue(), ^{
// 4.将新图片显示出来
self.imageView.image = image;
});
});
} /**
* 快速迭代:dispatch_apply(subpaths.count, queue, ^(size_t index):快速迭代函数:第一个参数为迭代对象的个数,第二个参数为队列,index为返回的索引,快速迭代,并发执行,提高迭代效率
*/
- (void)apply
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, );
NSString *from = @"/Users/xiaomage/Desktop/From";
NSString *to = @"/Users/xiaomage/Desktop/To"; NSFileManager *mgr = [NSFileManager defaultManager];
NSArray *subpaths = [mgr subpathsAtPath:from]; dispatch_apply(subpaths.count, queue, ^(size_t index) {
NSString *subpath = subpaths[index];
NSString *fromFullpath = [from stringByAppendingPathComponent:subpath];
NSString *toFullpath = [to stringByAppendingPathComponent:subpath];
// 剪切
[mgr moveItemAtPath:fromFullpath toPath:toFullpath error:nil]; NSLog(@"%@---%@", [NSThread currentThread], subpath);
});
} /**
* 传统文件剪切
*/
- (void)moveFile
{
NSString *from = @"/Users/xiaomage/Desktop/From";
NSString *to = @"/Users/xiaomage/Desktop/To"; NSFileManager *mgr = [NSFileManager defaultManager];
//此方法获得的是from路径下的所有文件路径,包括子文件夹下的文件路径
NSArray *subpaths = [mgr subpathsAtPath:from]; for (NSString *subpath in subpaths) {
NSString *fromFullpath = [from stringByAppendingPathComponent:subpath];
NSString *toFullpath = [to stringByAppendingPathComponent:subpath];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
// 将文件从一个文件夹移动到另一个文件夹:是个耗时的操作,座椅在子线程中执行
[mgr moveItemAtPath:fromFullpath toPath:toFullpath error:nil];
});
}
}
/**
* 一次性函数:在整个项目中只执行一次
*/
- (void)once
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"------run");
});
} /**
* 延迟执行:GCD延迟函数:dispatch_after所传的队列不同,就在不同的队列中执行延迟函数,延迟的方法有以下三种
*/
- (void)delay
{
NSLog(@"touchesBegan-----");
// [self performSelector:@selector(run) withObject:nil afterDelay:2.0]; // dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// NSLog(@"run-----");
// }); [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:NO];
} - (void)run
{
NSLog(@"run-----");
} /**
* dispatch_barrier_async函数:能保证先12任务先执行完,在执行dispatch_barrier_async中的任务,最后在执行34任务,要想此函数起作用,queue不能是全局并发队列,因为queue是并发队列,所以12,34执行完的先后顺序不确定,但是能保证先执行12,在执行dispatch_barrier_async函数任务,最后执行34
*/
- (void)barrier
{
dispatch_queue_t queue = dispatch_queue_create("", 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]);
});
} @end

7:GCD下完整的单例模式:GCD模式下的单例保证了线程的安全,dispatch_once是线程安全的,防止同一时间多条线程初始化同一个变量

#import "XMGPerson.h"

@interface XMGPerson() <NSCopying>

@end

@implementation XMGPerson

/**
* 模拟三种情况得到单例对象:
1:alloc创建对象 2:通过对象copy得到一个新对象 3:直接调用sharedPerson得到一个新对象
2:1:先用static定义一个下划线的成员变量 2:alloc方法内部会调用allocWithZone方法,用dispatch_once函数调用父类[super allocWithZone:zone]去开辟空间 3:当在类中实现copyWithZone方法时,需要类去遵守NSCopying协议,直接将成员变量返回,是因为调用copy之前该对象已经被初始化了
*/
static XMGPerson *_person; + (instancetype)allocWithZone:(struct _NSZone *)zone
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_person = [super allocWithZone:zone];
});
return _person;
} + (instancetype)sharedPerson
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_person = [[self alloc] init];
});
return _person;
} - (id)copyWithZone:(NSZone *)zone
{
return _person;
}
@end

8:单例的宏定义:其中的\表示下一行也属于宏定义

// .h文件
#define XMGSingletonH(name) + (instancetype)shared##name; // .m文件
#define XMGSingletonM(name) \
static id _instance; \
\
+ (instancetype)allocWithZone:(struct _NSZone *)zone \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instance = [super allocWithZone:zone]; \
}); \
return _instance; \
} \
\
+ (instancetype)shared##name \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instance = [[self alloc] init]; \
}); \
return _instance; \
} \
\
- (id)copyWithZone:(NSZone *)zone \
{ \
return _instance; \
}

9:非GCD模式下的单例:加锁的目的是保证线程安全,防止同一时间多条线程访问初始化同一个变量

#import "XMGPerson.h"

@interface XMGPerson()

@end

@implementation XMGPerson

static id _instance;

+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
@synchronized(self) {
if (_instance == nil) {
_instance = [super allocWithZone:zone];
}
}
return _instance;
} + (instancetype)sharedInstance
{
@synchronized(self) {
if (_instance == nil) {
_instance = [[self alloc] init];
}
}
return _instance;
} - (id)copyWithZone:(NSZone *)zone
{
return _instance;
}
@end

ios开发之多线程---GCD的更多相关文章

  1. IOS开发之多线程 -- GCD的方方面面

    前言:这篇GCD的博文是本人阅读了很多海内外大神的关于GCD的文章,以及结合之前自己对GCD的粗浅的认识,然后取其精华,去其槽粕,综合起来的笔记,而且是尽可能的以通熟易懂的并且是正确的理论论述方式呈现 ...

  2. iOS开发-多线程编程技术(Thread、Cocoa operations、GCD)

    简介 在软件开发中,多线程编程技术被广泛应用,相信多线程任务对我们来说已经不再陌生了.有了多线程技术,我们可以同做多个事情,而不是一个一个任务地进行.比如:前端和后台作交互.大任务(需要耗费一定的时间 ...

  3. iOS开发之多线程(NSThread、NSOperation、GCD)

    整理一些多线程相关的知识. 并行 & 并发 1.并行:并行是相对于多核而言的,几个任务同时执行.2.并发:并发是相对于单核而言的,几个任务之间快速切换运行,看起来像是"同时" ...

  4. iOS开发之多线程技术

    本篇争取一篇讲清讲透,依然将通过四大方面清晰的对iOS开发中多线程的用法进行详尽的讲解: 一.什么是多线程 1)多线程执行原理 2)线程与进程 3)多线程的优缺点 二.我们为什么要用多线程编程技术 三 ...

  5. iOS开发-多线程开发之线程安全篇

    前言:一块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源,比如多个线程访问同一个对象.同一个变量.同一个文件和同一个方法等.因此当多个线程访问同一块资源时,很容易会发生数据错误及数据不安 ...

  6. iOS开发之多线程技术(NSThread、OperationQueue、GCD)

    在前面的博客中如果用到了异步请求的话,也是用到的第三方的东西,没有正儿八经的用过iOS中多线程的东西.其实多线程的东西还是蛮重要的,如果对于之前学过操作系统的小伙伴来说,理解多线程的东西还是比较容易的 ...

  7. iOS开发之多线程技术——GCD篇

    本篇将从四个方面对iOS开发中GCD的使用进行详尽的讲解: 一.什么是GCD 二.我们为什么要用GCD技术 三.在实际开发中如何使用GCD更好的实现我们的需求 一.Synchronous & ...

  8. iOS开发之多线程技术—GCD篇

    本篇将从四个方面对iOS开发中GCD的使用进行详尽的讲解: 一.什么是GCD 二.我们为什么要用GCD技术 三.在实际开发中如何使用GCD更好的实现我们的需求 一.Synchronous & ...

  9. iOS开发之多线程技术——NSOperation篇

    本篇将从四个方面对iOS开发中使用到的NSOperation技术进行讲解: 一.什么是NSOperation 二.我们为什么使用NSOperation 三.在实际开发中如何使用NSOperation ...

随机推荐

  1. Error: org.apache.mahout.math.CardinalityException: Required cardinality 10 but got 30问题解决办法

    问题详情 在运行mahout中kmeans算法时,采取的是其默认输入路径/user/hadoop/testdata 和 默认输出路径/user/hadoop/output. [hadoop@djt00 ...

  2. $routeParams 实现路由指定参数

    [摘要]后台管理系统权限控制到按钮级别,将每一个资源的key绑定在url中,渲染页面的时候去根据key来获取当前页面的按钮列表. router.js angular.module("app. ...

  3. 【Thinkphp学习】TP3.2.3在PHP5.5环境下运行非常慢

    在做项目时遇到了一个瓶颈问题:老项目迁移到PHP5.5环境后打开网页很卡很慢. 服务器环境为:apache+php5.5.38+mysql,使用框架为Thinkphp3.2.3. 经过多方面排查找到了 ...

  4. APK文件浅析-Android

    2011~2015,5年时间,断断续续学习了Android.  最近打算在2011年2个月认真学习的基础上,深入学习下.  由于有之前的Android基础,加上N年的Java等变成经验,自我感觉And ...

  5. CISP/CISA 每日一题 18

    CISSP 每日一题(答)What is the purpose of an access review and audit? Checkto ensure that users do not hav ...

  6. [React] Render Elements Outside the Current React Tree using Portals in React 16

    By default the React Component Tree directly maps to the DOM Tree. In some cases when you have UI el ...

  7. ubuntu14中 memcached安装与使用

    第一步,先安装lib-event 下载lib-event  的包http://libevent.org/ 下载完之后,解压安装 ./configure –prefix=/usr (或 ./config ...

  8. JQuery滑动到指定位置

    $('html, body').animate({ scrollTop: next_tip.offset().top + "px"},500);

  9. HorizontalListView中使用notifyDataSetChanged()和notifyDataSetInvalidated()

    今天在项目中用到了水平ListView控件HorizontalListView,也是我在网上找的个开源HorizontalListView直接在项目中使用.我是把HorizontalListView放 ...

  10. Javascript和jquery事件--事件对象event

    1.  事件对象event 对于event,js的解释是Event 对象代表事件的状态,比如事件在其中发生的元素.键盘按键的状态.鼠标的位置.鼠标按钮的状态.而jq的解释是事件处理(事件对象.目标元素 ...