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开发多线程--技术方案的更多相关文章

  1. iOS开发 - 多线程实现方案之Pthread篇

    pthread基础 pthread是POSIX thread的简写,一套通用的多线程API,适用于Unix.Linux.Windows等系统,跨平台.可移植,使用难度大,C语言框架,线程生命周期由程序 ...

  2. iOS开发 - 多线程实现方案之GCD篇

    GCD概念 GCD为Grand Central Dispatch的缩写,纯c语言编写,是Apple开发的一个多核编程的较新的解决方法.它主要用于优化应用程序以支持多核处理器以及其他对称多处理系统.它是 ...

  3. iOS开发 - 多线程实现方案之NSThread篇

    NSThread API //类方法:创建一个线程 + (void)detachNewThreadWithBlock:(void (^)(void))block API_AVAILABLE(macos ...

  4. iOS开发 - 多线程实现方案之NSOperation篇

    NSOperation简介 1.实现多线程编程步骤: 配合使用NSOperation和NSOperationQueue实现多线程编程,我们不用考虑线程的生命周期.同步.加锁等问题,如下: 先将需要执行 ...

  5. iOS多线程技术方案

    iOS多线程技术方案 目录 一.多线程简介 1.多线程的由来 2.耗时操作的模拟试验 3.进程和线程 4.多线程的概念及原理 5.多线程的优缺点和一个Tip 6.主线程 7.技术方案 二.Pthrea ...

  6. iOS开发多线程篇—多线程简单介绍

    iOS开发多线程篇—多线程简单介绍 一.进程和线程 1.什么是进程 进程是指在系统中正在运行的一个应用程序 每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内 比如同时打开QQ.Xcod ...

  7. iOS开发多线程篇—线程安全

    iOS开发多线程篇—线程安全 一.多线程的安全隐患 资源共享 1块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源 比如多个线程访问同一个对象.同一个变量.同一个文件 当多个线程访问同一块 ...

  8. iOS开发——多线程OC篇&多线程详解

    多线程详解 前面介绍了多线程的各种方式及其使用,这里补一点关于多线程的概念及相关技巧与使用,相信前面不懂的地方看了这里之后你就对多线程基本上没有什么问题了! 1——首先ios开发多线程中必须了解的概念 ...

  9. iOS开发多线程篇—多线程简介

    iOS开发多线程篇-多线程简介 一.进程和线程 1.什么是进程 进程是指在系统中正在执行的一个应用程序 每一个进程之间是独立的.每一个进程均执行在其专用且受保护的内存空间内 比方同一时候打开QQ.Xc ...

随机推荐

  1. STM32管教复用与重映射关系

    摘自:http://blog.csdn.net/lincheng15/article/details/51789093 概括一下:复用就是一个引脚有几个功能,1.做普通IO输入输出 2.其他外设的输入 ...

  2. 内核中的 likely() 与 unlikely()

    内核中的 likely() 与 unlikely() 在 2.6 内核中,随处可以见到 likely() 和 unlikely() 的身影,那么为什么要用它们?它们之间有什么区别? 首先要明确: if ...

  3. 我教女朋友学编程html系列(7)—Html无序列表、自定义列表、有序列表及常用例子

    昨天写的那篇文章<我教女朋友学编程Html系列(6)—Html常用表单控件>,基本上有1000人左右看了,那边文章是我站在前人的肩膀上修改来的,添加了截图和说明,合并了例子,使之更容易被初 ...

  4. vi中正则表达式的使用

    在当前行中删除从aa到zz的所有字符 :s/aa.*zz//在整个文件用and代替所有的&字符:1,$s/&/and/g在每一行的首行插入字符串new:1,$s/^/new/g在第二行 ...

  5. HDU 5592 ZYB's Premutation

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5592 题意: http://bestcoder.hdu.edu.cn/contests/contes ...

  6. SqlServer Split函数

    Create FUNCTION [dbo].[SplitToTable] ( @SplitString nvarchar(max), @Separator nvarchar(10)=' ' ) RET ...

  7. bzoj 2002 LCT

    LCT最基础的题,就用到了一个ACCESS操作 首先我们将这个绵羊弹飞的情况看成一颗树,那么假设X点被弹飞到 Y点,那么Y为X的父亲节点,弹飞的话父亲节点为n+1(虚设) 那么每个询问就是询问X点到根 ...

  8. HDU 2196 求树上所有点能到达的最远距离

    其实我不是想做这道题的...只是今天考试考了一道类似的题...然后我挂了... 但是乱搞一下还是有80分....可惜没想到正解啊! 所以今天的考试题是: 巡访 (path.pas/c/cpp) Cha ...

  9. PowerDesigner(六)-物理数据模型(PDM逆向工程)(转)

    物理数据模型PDM 物理数据模型(Physical Data Model,PDM):在数据库的逻辑结构设计好之后,就需要完成其物理设计,PDM就是为实现这一目的而设计的. 物理数据模型是以常用的DBM ...

  10. 用WebStorm编辑Markdown

    前言[转http://blog.fens.me/webstorm-markdown/] WebStorm是一个非常高效的开发工具,特别对于Web前端的开发.Markdown流行,因为其简化了HTML的 ...