iOS开发多线程--技术方案
pthread 实现多线程操作
代码实现:
void * run(void *param)
{
for (NSInteger i = 0; i < 1000; i++) {
NSLog(@"---buttonclick---%zd---%@", i, [NSThread currentThread]);
}
return NULL;
}
@implementation ViewController
- (IBAction)clickButton:(id)sender {
// 定义一个线程
pthread_t thread;
// 创建一个线程 (参1)pthread_t *restrict:创建线程的指针,(参2)const pthread_attr_t *restrict:线程属性 (参3)void *(*)(void *):线程执行的函数的指针,(参4)void *restrict:null
pthread_create(&thread, NULL, run, NULL);
// 何时回收线程不需要你考虑
pthread_t thread2;
pthread_create(&thread2, NULL, run, NULL);
}
NSThread实现多线程
一个 NSThread 对象就代表一条线程
创建线程的多种方式
第一种方式:先创建再启动线程
// 创建线程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"jack"]; // 线程启动了,事情做完了才会死, 一个NSThread对象就代表一条线程
[thread start];第二种:直接创建并启动线程
// 直接创建并启动线程
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"jack"];第三种:
// 直接创建并启动线程
[self performSelectorInBackground:@selector(run:) withObject:@"jack"]; // 使线程进入阻塞状态
[NSThread sleepForTimeInterval:2.0]; #pragma mark - 执行run方法
- (void)run:(NSString *)param
{
// 当前线程是否是主线程
for (NSInteger i = 0; i < 100; i++) {
NSLog(@"---%@---%zd---%d", [NSThread currentThread], i, [NSThread isMainThread]);
}
}方法2和方法3的优点:快捷 方法1的优点:可以轻松拿到线程
线程间通信
线程间通信的体现
1个线程传递数据给另1个线程
在1个线程中执行完特定任务后,转到另1个线程继续执行任务
线程间通信的常用方法:小程序图片下载
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// 获取图片的url
NSURL *url = [NSURL URLWithString:@"http://7xjanq.com1.z0.glb.clouddn.com/6478.jpg"];
// 另开1条线程 object用于数据的传递
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(downLoadWithURL:) object:url];
// 由于下面下载图片的耗时太长,应领开启线程来完成
[thread start];
}
// 下载图片
- (void)downLoadWithURL:(NSURL *)url
{
NSLog(@"%@", [NSThread currentThread]);
// 下载图片
NSData *data = [NSData dataWithContentsOfURL:url];
// 生成图片
UIImage *image = [UIImage imageWithData:data];
// 返回主线程显示图片
[self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];
}
以上两种方式使用线程已经过时了,开发中我们操作线程大多都使用 GCD 和 NSOperation 来实现多线程操作。
下面我就给大家系统的介绍一下 GCD 是如何实现多线程的
GCD 实现多线程
GCD 简介
GCD 全称是Grand Central Dispatch,可译为“超级厉害的中枢调度器”,GCD 是苹果公司为多核的并行运算提出的解决方案, GCD会自动利用更多的 CPU 内核(比如双核、四核)来开启线程执行任务,GCD 会自动管理线程的生命周期(创建线程、调度任务、销毁线程),不需要我们程序员手动管理内存。
任务和队列
任务:在同步函数和异步函数中执行
队列:用来存放任务(并发 串行)
GCD会自动将队列中的任务取出,放到对应的线程,任务的取出遵循FIFO,即先入先出队列,First Input First Output 的缩写。先进入的任务先完成并结束,再执行后面的任务。
同步函数和异步函数,并发队列和串行队列
用同步的方式执行任务:在当前线程中可立即执行任务,不具备开启线程的能力
用异步的方式执行任务:在当前线程结束时执行任务,具备开启新的线程的能力
并发队列:允许多个任务同时执行
串行队列:一个任务执行完毕后,再执行下一个任务
创建并发/串行队列代码:
// 创建并发队列
// 参1:const char *label 队列名称
// 参2:dispatch_queue_attr_t attr 队列类型
dispatch_queue_t queueConcurrent = dispatch_queue_create("520it.com", DISPATCH_QUEUE_CONCURRENT);
// 创建串行队列 serial 串行 concurrent并发
dispatch_queue_t queueSerial = dispatch_queue_create("520it.com", DISPATCH_QUEUE_SERIAL);
// 获取全局队列 全局队列是并发队列
// 参1:队列的优先级
// 参2:0(以后可能用到的参数)
dispatch_queue_t queueGlobal = 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_MIN // 后台
// 获取主队列 在主队列中的任务都会在主线程中执行。
dispatch_queue_t queueMain = dispatch_get_main_queue();
同步/异步函数代码表示:
// GCD同步函数串行队列(立即执行,当前线程)
// 参1: dispatch_queue_t queue 队列
// 参2: 任务
dispatch_sync(queueSerial, ^{
for (NSInteger i = 0; i < 10; i++) {
NSLog(@"~~~%@", [NSThread currentThread]);
}
});
// 同步函数并行队列(立即执行,当前线程)
dispatch_sync(queueConcurrent, ^{
for (NSInteger i = 0; i < 10; i++) {
NSLog(@"~~~%@", [NSThread currentThread]);
}
});
// 异步函数串行队列 (另开线程,多个任务按顺序执行)
dispatch_async(queueSerial, ^{
dispatch_async(queueSerial, ^{
for (NSInteger i = 0; i < 10; i++) {
NSLog(@"~~~%@", [NSThread currentThread]);
}
});
dispatch_async(queueSerial, ^{
for (NSInteger i = 0; i < 10; i++) {
NSLog(@"~~~%@", [NSThread currentThread]);
}
});
dispatch_async(queueSerial, ^{
for (NSInteger i = 0; i < 10; i++) {
NSLog(@"~~~%@", [NSThread currentThread]);
}
});
});
// 异步函数并行队列 (另开线程,多个任务一起执行)
dispatch_async(queueConcurrent, ^{
dispatch_async(queueSerial, ^{
for (NSInteger i = 0; i < 10; i++) {
NSLog(@"~~~%@", [NSThread currentThread]);
}
});
dispatch_async(queueSerial, ^{
for (NSInteger i = 0; i < 10; i++) {
NSLog(@"~~~%@", [NSThread currentThread]);
}
});
dispatch_async(queueSerial, ^{
for (NSInteger i = 0; i < 10; i++) {
NSLog(@"~~~%@", [NSThread currentThread]);
}
});
});
// 主队列:(任何一个任务只要在主队列中,都会加入到主线程的队列中执行)
注意:使用sync函数(同步函数)往当前串行队列中添加任务,会卡住当前的串行队列
解释:使用同步函数添加任务 A 到串行队列,说明要在当前串行队列立即执行任务 A ,任务 A 执行完后,才会执行任务 A 后面的代码。但当前队列是串行队列,也就是说任务 A 必须等到当前串行队列中正在执行的任务 B 完成之后才能执行,因此又必须先执行任务 A 中立即执行任务,又要必须等到任务 B 执行完以后才能执行下一个任务,所以就会卡死。你等我,我等你,谁也无法执行。
GCD实现线程通信
小项目:下载图片
代码如下:
// 获取图片的url
NSURL *url = [NSURL URLWithString:@"http://7xjanq.com1.z0.glb.clouddn.com/6478.jpg"];
// 开启线程下载图片
dispatch_queue_t queue = dispatch_queue_create("111", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
// 下载完成后返回主线程显示图片
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
});
GCD其他常用函数
dispatch_barrier 栅栏
// 1.barrier : 在barrier前面的先执行,然后再执行barrier,然后再执行barrier后面的 barrier的queue不能是全局的并发队列
dispatch_queue_t queue = dispatch_queue_create("11", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
for (int i = 0; i < 100; i++) {
NSLog(@"%@--1", [NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 100; i++) {
NSLog(@"%@--2", [NSThread currentThread]);
}
});
dispatch_barrier_async(queue, ^{
for (int i = 0; i < 100; i++) {
NSLog(@"%@--3", [NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 100; i++) {
NSLog(@"%@--4", [NSThread currentThread]);
}
});
dispatch_after 延迟执行
// 延迟执行
// 方法1
[self performSelector:@selector(run:) withObject:@"参数" afterDelay:2.0];
// 方法2
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
for (NSInteger i = 0; i < 100; i++) {
NSLog(@"%@", [NSThread currentThread]);
}
});
// 方法3
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:NO];
dispatch_once 整个程序运行中执行一次
// 整个程序中只执行一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 一次性代码
});
作用:实现某个类的单粒对象
单例模式:在整个应用程序中,共享一份资源(这份资源只需要创建初始化1次)
static id _person;
+ (instancetype)sharePerson
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_person = [[super alloc] init];
});
return _person;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_person = [super allocWithZone:zone];
});
return _person;
}
- (id)copy
{
return _person;
}
开发中一般自定义成宏,比较方便,一行代码搞定。
dispatch_apply 快速迭代
示例小程序:将一个文件夹中的图片剪切到另一个文件夹
// 将图片剪切到另一个文件夹里
NSString *from = @"/Users/Ammar/Pictures/壁纸";
NSString *to = @"/Users/Ammar/Pictures/to";
NSFileManager *manager = [NSFileManager defaultManager];
NSArray *subPaths = [manager subpathsAtPath:from];
// 快速迭代
dispatch_apply(subPaths.count, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t index) {
NSLog(@"%@ - %zd", [NSThread currentThread], index);
NSString *subPath = subPaths[index];
NSString *fromPath = [from stringByAppendingPathComponent:subPath];
NSString *toPath = [to stringByAppendingPathComponent:subPath];
// 剪切
[manager moveItemAtPath:fromPath toPath:toPath error:nil];
NSLog(@"%@---%zd", [NSThread currentThread], index);
});
dispatch_group 队列组
示例小程序:需求下载图片1 下载图片2 将图片1和图片2合成新的图片
// 创建队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 创建组
dispatch_group_t group = dispatch_group_create();
// 用组队列下载图片1
dispatch_group_async(group, queue, ^{
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://7xjanq.com1.z0.glb.clouddn.com/6478.jpg"]];
self.image1 = [UIImage imageWithData:data];
NSLog(@"1%@", [NSThread currentThread]);
});
// 用组队列下载图片2
dispatch_group_async(group, queue, ^{
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://7xjanq.com1.z0.glb.clouddn.com/6478.jpg"]];
self.image2 = [UIImage imageWithData:data];
NSLog(@"2%@", [NSThread currentThread]);
});
// 将图片1和图片2合成一张图片
dispatch_group_notify(group, queue, ^{
CGFloat imageW = self.imageView.bounds.size.width;
CGFloat imageH = self.imageView.bounds.size.height;
// 开启位图上下文
UIGraphicsBeginImageContext(self.imageView.bounds.size);
// 画图
[self.image1 drawInRect:CGRectMake(0, 0, imageW * 0.5, imageH)];
[self.image2 drawInRect:CGRectMake(imageW * 0.5, 0, imageW * 0.5, imageH)];
// 将图片取出
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 关闭图形上下文
UIGraphicsEndImageContext();
// 在主线程上显示图片
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
NSLog(@"3%@", [NSThread currentThread]);
});
GCD定时器
GCD定时器不受Mode影响因此比NSTimer要准确
static int count = 0;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 这句话的意思现在很好懂了
});
// GCD定时器
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 1.创建一个定时器源
// 参1:类型定时器
// 参2:句柄
// 参3:mask传0
// 参4:队列 (注意:dispatch_source_t本质是OC对象,表示源)
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
// 严谨起见,时间间隔需要用单位int64_t,做乘法以后单位就变了
// 下面这句代码表示回调函数时间间隔是多少
int64_t interval = (int64_t)(2.0 * NSEC_PER_SEC);
// 如何设置开始时间 CGD给我们了一个设置时间的方法
// 参1:dispatch_time_t when 传一个时间, delta是增量
dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)); // 从现在起3秒后开始
// 2.设置定时器的各种属性
// 参1:timer
// 参2:开始时间
// 参3:时间间隔
// 参4:传0 不需要 DISPATCH_TIME_NOW 表示现在 GCD 时间用 NS 表示
dispatch_source_set_timer(self.timer, start, interval, 0);
// 3.设置回调(即每次间隔要做什么事情)
dispatch_source_set_event_handler(self.timer, ^{
NSLog(@"----------------%@", [NSThread currentThread]);
// 如果希望做5次就停掉
count++;
if (count == 5) {
dispatch_cancel(self.timer);
self.timer = nil;
}
});
// 4.启动定时器 (恢复)
dispatch_resume(self.timer);
讲完 GCD 就该讲讲 NSOperation,它是 GCD 的面向对象的封装,使用起来也更方便,
NSOperation实现多线程
NSOperation是个抽象类,并不具备封装操作的能力,必须使用它的子类
NSInvocationOperation
NSBlockOperation
自定义子类继承NSOperation,实现内部相应的方法
使用 NSOperation 实现多线程的步骤:
创建任务 NSOperation 对象
创建 NSOperationQueue 队列
将任务 NSOperation 对象 add 到 NSOperationQueue 队列中去
NSInvocationOperation
代码如下:
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
[op start];
注意:默认情况下,调用了start方法后并不会开一条新的线程去执行,而是在当前线程同步执行操作,只有将 NSOperation 放到一个 NSOperationQueue 中,才会异步执行操作
NSBlockOperation
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
// 在主线程
NSLog(@"下载1------%@", [NSThread currentThread]);
}];
// 添加额外的任务(在子线程执行),封装数大于1才会异步执行
[op addExecutionBlock:^{
NSLog(@"下载2------%@", [NSThread currentThread]);
}];
自定义Operation:需要实现- (void)main方法,需要做的事情放在mian方法中
NSOperationQueue
使用NSOperationQueue创建队列:主队列和全局队列
// 创建一个其他队列(包括串行队列和并发队列) 放到这个队列中的NSOperation对象会自动放到子线程中执行
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 创建一个主队列,放到这个队列中的NSOperation对象会自动放到子线程中执行
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
// 表示并发数量:即同时执行任务的最大数。
queue.maxConcurrentOperationCount = 1;
队列的取消、暂停、恢复:
// NSOpertion的 - cancel 方法也可以停止单个操作
- (void)cancelAllOperations;
// YES代表暂停队列,NO代表恢复队列
- (void)setSuspended:(BOOL)b;
添加依赖
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *block1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download1 -------------- %@", [NSThread currentThread]);
}];
NSBlockOperation *block2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download2 -------------- %@", [NSThread currentThread]);
}];
NSBlockOperation *block3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download3 -------------- %@", [NSThread currentThread]);
}];
// 添加依赖: block1 和 block2执行完后 再执行 block3 block3依赖于block1和block2
// 给block3添加依赖 让block3在block1和block2之后执行
[block3 addDependency:block1];
[block3 addDependency:block2];
[queue addOperation:block1];
[queue addOperation:block2];
[queue addOperation:block3];
注意:不能循环依赖,但可以跨队列依赖,不管NSOperation对象在哪个队列。只要是两个NSOperation对象就可以依赖
线程间通信
示例:下载图片
// 下载图片 operation实现线程间通信
[[[NSOperationQueue alloc] init] addOperation:[NSBlockOperation blockOperationWithBlock:^{
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://7xjanq.com1.z0.glb.clouddn.com/6478.jpg"]]];
// 返回主线程
[[NSOperationQueue mainQueue] addOperation:[NSBlockOperation blockOperationWithBlock:^{
self.imageView.image = image;
}]];
}]];
示例:下载图片1和图片2 并合成图片
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 下载图片1
__block UIImage *image1 = nil;
NSBlockOperation *block1 = [NSBlockOperation blockOperationWithBlock:^{
image1 = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://7xjanq.com1.z0.glb.clouddn.com/6478.jpg"]]];
}];
// 下载图片2
__block UIImage *image2 = nil;
NSBlockOperation *block2 = [NSBlockOperation blockOperationWithBlock:^{
image2 = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://7xjanq.com1.z0.glb.clouddn.com/6478.jpg"]]];
}];
CGFloat imageW = self.imageView.bounds.size.width;
CGFloat imageH = self.imageView.bounds.size.height;
// 合成图片
NSBlockOperation *block3 = [NSBlockOperation blockOperationWithBlock:^{
UIGraphicsBeginImageContext(CGSizeMake(imageW, imageH));
[image1 drawInRect:CGRectMake(0, 0, imageW * 0.5, imageH)];
[image2 drawInRect:CGRectMake(0.5 * imageW, 0, 0.5 * imageW, imageH)];
UIImage *image3 = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// 切换回主线程显示图片
[[NSOperationQueue mainQueue] addOperation:[NSBlockOperation blockOperationWithBlock:^{
self.imageView.image = image3;
}]];
}];
// 设置依赖
[block3 addDependency:block1];
[block3 addDependency:block2];
// 添加任务到队列中
[queue addOperation:block1];
[queue addOperation:block2];
[queue addOperation:block3];
应用
应用:SDWebImage 框架的底层主要功能实现就是基于多线程,使用多线程,我们可以实现小图片的多图片下载。这里的逻辑其实是比较复杂的
实现小图片的多图片下载思路:
代码实现见本文代码。
本文代码见:Multithreading
https://github.com/lizhaoLoveIT/Multithreading
iOS开发多线程--技术方案的更多相关文章
- iOS开发 - 多线程实现方案之Pthread篇
pthread基础 pthread是POSIX thread的简写,一套通用的多线程API,适用于Unix.Linux.Windows等系统,跨平台.可移植,使用难度大,C语言框架,线程生命周期由程序 ...
- iOS开发 - 多线程实现方案之GCD篇
GCD概念 GCD为Grand Central Dispatch的缩写,纯c语言编写,是Apple开发的一个多核编程的较新的解决方法.它主要用于优化应用程序以支持多核处理器以及其他对称多处理系统.它是 ...
- iOS开发 - 多线程实现方案之NSThread篇
NSThread API //类方法:创建一个线程 + (void)detachNewThreadWithBlock:(void (^)(void))block API_AVAILABLE(macos ...
- iOS开发 - 多线程实现方案之NSOperation篇
NSOperation简介 1.实现多线程编程步骤: 配合使用NSOperation和NSOperationQueue实现多线程编程,我们不用考虑线程的生命周期.同步.加锁等问题,如下: 先将需要执行 ...
- iOS多线程技术方案
iOS多线程技术方案 目录 一.多线程简介 1.多线程的由来 2.耗时操作的模拟试验 3.进程和线程 4.多线程的概念及原理 5.多线程的优缺点和一个Tip 6.主线程 7.技术方案 二.Pthrea ...
- iOS开发多线程篇—多线程简单介绍
iOS开发多线程篇—多线程简单介绍 一.进程和线程 1.什么是进程 进程是指在系统中正在运行的一个应用程序 每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内 比如同时打开QQ.Xcod ...
- iOS开发多线程篇—线程安全
iOS开发多线程篇—线程安全 一.多线程的安全隐患 资源共享 1块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源 比如多个线程访问同一个对象.同一个变量.同一个文件 当多个线程访问同一块 ...
- iOS开发——多线程OC篇&多线程详解
多线程详解 前面介绍了多线程的各种方式及其使用,这里补一点关于多线程的概念及相关技巧与使用,相信前面不懂的地方看了这里之后你就对多线程基本上没有什么问题了! 1——首先ios开发多线程中必须了解的概念 ...
- iOS开发多线程篇—多线程简介
iOS开发多线程篇-多线程简介 一.进程和线程 1.什么是进程 进程是指在系统中正在执行的一个应用程序 每一个进程之间是独立的.每一个进程均执行在其专用且受保护的内存空间内 比方同一时候打开QQ.Xc ...
随机推荐
- Jackson如何使JSON输出变得优雅?
本篇文章翻译自:How to enable pretty print JSON output (Jackson) 在这篇文章中,我们将教你如何利用Jackson Library在控制台或者JSP页面优 ...
- 自定义 tabBar (默认 tabBar 为可读不可写类型)
KVC 方法 //由于 tabBar是只读 不能够直接操作,如果要修改 可以使用KVC let mainTabBar = MainTabBar() //KVC 赋值 setValue(mainTab ...
- .net sql 防注入 httpmodule
1 新建一个类,实现IHttpModule接口 using System; using System.Collections.Generic; using System.Linq; using Sys ...
- “我爱淘”冲刺阶段Scrum站立会议1
昨天是我们项目冲刺阶段的第一天,站立会议的内容如下: 1.昨天完成了项目中的第一个界面--“精选”界面:完成了一点Java文件的编写: 2.今天的任务就是完成第一个Activity的编写:将布局文件和 ...
- MVC缓存技术
一.MVC缓存简介 缓存是将信息(数据或页面)放在内存中以避免频繁的数据库存储或执行整个页面的生命周期,直到缓存的信息过期或依赖变更才再次从数据库中读取数据或重新执行页面的生命周期.在系统优化过程中, ...
- Ombrophobic Bovines - POJ 2391
Description FJ's cows really hate getting wet so much that the mere thought of getting caught in the ...
- 老陈 WPF
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.W ...
- HDU 5597 GTW likes function 欧拉函数
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5597 题意: http://bestcoder.hdu.edu.cn/contests/contes ...
- BZOJ 4443: [Scoi2015]小凸玩矩阵 二分图最大匹配+二分
题目链接: http://www.lydsy.com/JudgeOnline/problem.php?id=4443 题解: 二分答案,判断最大匹配是否>=n-k+1: #include< ...
- 设计模式之外观模式(Facade)
外观模式原理:将复杂的子系统的结构封装起来,只提供客户一个简单的接口 代码如下: #include <iostream> #include <string> #include ...