iOS GCD集汇(一)GCD(Grand Central Dispatch)和Block 使用-浅析

2024-03-16 00:48

本文主要是介绍iOS GCD集汇(一)GCD(Grand Central Dispatch)和Block 使用-浅析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

@原文地址:http://blog.sina.com.cn/s/blog_7b9d64af0101c75e.html

Grand Central Dispatch,或者简称 GCD,是一个与 Block Object 产生工作的低级的 C API。GCD 真正的用途是将任务分配到多个核心又不让程序员担心哪个内核执行哪个任务。 在 Max OS X 上,多内核设备,包括笔记本,用户已经使用了相当长的时间。通过多核设备 比如 iPad2 的介绍,程序员能为 iOS 写出神奇的多核多线程 APP。

GCD 的核心是分派队列。不论在 iOS 还是 Max OS X 分派队列,正如我们快看到的是 由位于主操作系统的 GCD 来管理的线程池。你不会直接与线程有工作关系。你只在分派队 列上工作,将任务分派到这个队列上并要求队列来调用你的任务。GCD 为运行任务提供了 几个选择:同步执行、异步执行和延迟执行等。


GCD+Block 可以是一对好兄弟啊!配合起来使用真的太犀利!
GCD和Block都是苹果后来推崇的方式。GCD,自我感觉主要封装了多线程编程的复杂性!而且,使用方便。
Block 主要就是语法类似C的一种块语言。对某一个功能控制有了更好的封装,比如可以调用对象内变量,无需再传参;可以在实现方法中直接写实现后的代码,而不用再进行委托跳转,使代码更加简洁。等等,好处多多吧,自己体会!

那就开始吧!
先看一个例子:

 

// 主线程队列,由系统自动创建并且与应用程序的主线程相关联。

    dispatch_queue_t mainQueue = dispatch_get_main_queue();

    

    // 要在主线程队列中,执行的Block

    dispatch_async(mainQueue, ^(void) {

        [[[UIAlertView alloc] initWithTitle:@"GCD"

                                    message:@"GCD is amazing!"

                                   delegate:nil cancelButtonTitle:@"OK"

                          otherButtonTitles:nilnilshow];

    });


dispatch_get_main_queue()

用来得到主队列。主队列又系统自动创建并且与应用程序主线程相关联。
也就是我们常说的能调用主线程修改UI的队列,即:主线程队列。

dispatch_async 方法

 void dispatch_async(dispatch_queue_t queue,dispatch_block_t block);


queue:队列
block:代码Block

根据指定的队列执行相应的Block。

dispatch_async_f 方法


void dispatch_async_f(dispatch_queue_t queue,void *context,dispatch_function_t work);


queue:指定执行该work的队列


void *context:所使用的 application-defined应用程序范围内有效的,也就是全局的)级别的参数。这是个C语法,void * 是一个无类型指针。也就是说,用它可以指向任何内存数据。


work:在指定队列(queue 参数)中要执行的方法。在该方法中,第一个参数所指代的数据,也就是dispatch_async_f方法所使用的第二个参数(void *context)所指带的数据。


例子:

// 定义结构体

typedef struct{

    char *title;

    char *message;

    char *cancelButtonTitle;

} AlertViewData;



// 定义dispatch_function_t 所执行的方法

void displayAlertView(void *paramContext){

    AlertViewData *alertData = (AlertViewData *)paramContext;

    

    NSString *title =[NSString stringWithUTF8String:alertData->title];

    NSString *message =[NSString stringWithUTF8String:alertData->message];

    NSString *cancelButtonTitle =[NSString stringWithUTF8String:alertData->cancelButtonTitle];

    

    [[[UIAlertView allocinitWithTitle:title message:message

                               delegate:nil

                      cancelButtonTitle:cancelButtonTitle otherButtonTitles:nilnil]show];

    

    

    // 释放结构体所占用的内存

    free(alertData);

    

}



// **执行dispatch_async_f

    

    dispatch_queue_t mainQueue = dispatch_get_main_queue();

    

    // 为结构体分配内存

    AlertViewData *context = (AlertViewData *) malloc(sizeof(AlertViewData));

    

    // 初始化结构体

    if (context != NULL){

        context->title = "GCD";

        context->message = "GCD is amazing.";

        context->cancelButtonTitle = "OK";

        

        // GCD执行异步方法:指定主队列(mainQueue),传递结构体数据(context)来执行displayAlertView方法。

        dispatch_async_f(mainQueue,(void *)context, displayAlertView);

    }


有了上面的说明铺垫,我想很快就能明白其中的含义了!

dispatch_async 方法


 void dispatch_async(dispatch_queue_t queue,dispatch_block_t block);


在指定的队列上,执行响应的Block。

例子:

dispatch_queue_t mainQueue1 = dispatch_get_main_queue();

    dispatch_async(mainQueue1, ^(void) {

        NSLog(@"Current thread = %@", [NSThread currentThread]);

        NSLog(@"Main thread = %@", [NSThread mainThread]);

    });



输出:

2013-03-27 15:45:54.501 DemoVideo[21802:707] Current thread = {name = (null), num = 1}

2013-03-27 15:45:54.503 DemoVideo[21802:707] Main thread = {name = (null), num = 1}


可见,dispatch_get_main_queue() 队列所执行的Block就是在主线程上执行的。


用GCD 同步执行Non-UI-Related 任务

同步:

例如,你想下载一个图片并想在下载完成之后展现给用 户。下载过程却和 UI 没有任何关系。对于任何与 UI 无关的任务,你可以使用 GCD 中的全 局并发队列。它们允许同步和异步执行。如果你同步提交一个任务到一个并发队列,同时提交另一个同步任务到另一个并发队列;相对而言这两个同步任务将异步运行,因为他们运行在两个不同的并发队 列上。你想确定在 B 任务开始之前 A 任务完 成了。那么必须把它们同时提交一个相同的队列。

dispatch_get_global_queue 方法


dispatch_queue_t dispatch_get_global_queue(long priority,unsigned long flags);


priority:优先级


flags:暂时还没有用到,为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_sync 方法


void dispatch_sync(dispatch_queue_t queue,dispatch_block_t block);


queue:指定的队列

block:执行的代码Block


在指定的队列中同步执行Block。同步是指,当循环执行某个Block时,当第一个Block执行完成以后,再执行下一个Block。

例子:

// 得到默认优先级队列

    dispatch_queue_t concurrentQueue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_sync(concurrentQueue, printFrom1To1000);// 同步执行

    dispatch_sync(concurrentQueue, printFrom1To1000);// 同步执行



// 同步执行的Block

void (^printFrom1To1000)(void) = ^{

    NSUInteger counter = 0;

    for (counter = 1;counter <= 1000;counter++){

        NSLog(@"Counter = %lu - Thread = %@",(unsigned long)counter, [NSThreadcurrentThread]);

    }

};



用GCD 异步执行Non-UI-Related 任务

异步:

在主队列、串行队列和并发队列上异步执行代码块才能见识到 GCD 的真正实力。你将会完全相信 GCD 是多线程应用的未来,并将完全取代 现代应用中的线程。 

我们换一种方式讲解异步。前面代码也稍微提了一些关于异步。

一般我们异步执行,需要用的两个方法为:

dispatch_async 方法
dispatch_async_f 方法

我们来一个综合的例子:

- (void) viewDidAppear:(BOOL)paramAnimated{

    dispatch_queue_t concurrentQueue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_async(concurrentQueue, ^{

        __block UIImage *image = nil;

        dispatch_sync(concurrentQueue, ^{

            // 同步下载图片

            NSString *urlAsString =@"http://images.apple.com/mobileme/features/images/ipad_findyouripad_20100518.jpg";

            NSURL *url = [NSURL URLWithString:urlAsString];

            NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];

            NSError *downloadError = nil;

            NSData *imageData = [NSURLConnection sendSynchronousRequest:urlRequest

                                                      returningResponse:nil

                                                                 error:&downloadError];

            if (downloadError == nil && imageData != nil){

                image = [UIImage imageWithData:imageData]; 

            }else if (downloadError != nil){

                NSLog(@"Error happened = %@", downloadError);

            } else {

                NSLog(@"No data could get downloaded from the URL."); }

        });

        

        dispatch_sync(dispatch_get_main_queue(), ^{

            // 直到下载图片完成,再调用主线程,更新UI

            if (image != nil){

                UIImageView *imageView = [[UIImageView alloc]initWithFrame:self.view.bounds];

                [imageView setImage:image];

                [imageView setContentMode:UIViewContentModeScaleAspectFit];

                [self.view addSubview:imageView];

            } else {

                NSLog(@"Image isn't downloaded. Nothing to display.");

            }

        });

    });

}


当页面显示时,用异步方式去下载图片,下载图片过程将同步执行,先下载图片数据,然后再调用主线程队列来更新UI。

用GCD延迟执行任务

如果,我们需要某个方法在某一段时间后,执行,那么我们常常会调用这样的方法:

- (void)viewDidLoad{

    [super viewDidLoad];

    

    [self performSelector:@selector(printString:)

               withObject:@"Grand Central Dispatch"

               afterDelay:3.0];

    

}


- (void) printString:(NSString *)paramString{

    NSLog(@"%@", paramString);

}


但是,我们要讲的时GCD方式。那么再GCD的世界里,也有类似的2个方法,来实现延迟执行的功能。

dispatch_after 方法

void dispatch_after(dispatch_time_t when,dispatch_queue_t queue,dispatch_block_t block);


when:所指定的时间


queue:指定的队列


block:执行的Block


将Block加入指定队列,并按照指定时间执行。

先上例子:

- (void)viewDidLoad{

    [super viewDidLoad];

    

    double delayInSeconds = 2.0;

    

    // 创建延期的时间 2S,因为dispatch_time使用的时间是纳秒,尼玛,比毫秒还小,太夸张了!!!

    dispatch_time_t delayInNanoSeconds =dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);

    // 得到全局队列

    dispatch_queue_t concurrentQueue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    // 延期执行

    dispatch_after(delayInNanoSeconds, concurrentQueue, ^(void){

        NSLog(@"Output GCD !");

        

    });

    

}


dispatch_time 方法


dispatch_time_t dispatch_time(dispatch_time_t when,int64_t delta);


when:指定的开始点,可以用 DISPATCH_TIME_NOW 来指定一个当前的时间点


delta纳秒数 


创建一个时间点( dispatch_time_t),返回的时间点为: delta+ when


dispatch_time_t:纯粹就是一个时间点。

dispatch_after 方法


void dispatch_after(dispatch_time_t when,dispatch_queue_t queue,dispatch_block_t block);


when:时间点


queue:指定的队列


block:执行的Block


将Block加入到指定的队列,并且在指定的时间点执行。

dispatch_after_f 方法


我想有了,前面知识的铺垫,这个方法理解起来,就像在切菜!!!

直接上例子:

- (void)viewDidLoad{

    [super viewDidLoad];

    

    double delayInSeconds = 2.0;

    dispatch_time_t delayInNanoSeconds =dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);

    dispatch_queue_t concurrentQueue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    

    dispatch_after_f(delayInNanoSeconds,concurrentQueue, @"GCD"processSomething);


}


void processSomething(void *paramContext){

    

    NSLog(@"This is %@",paramContext);

}


注意: dispatch_after_f 执行的是一个纯C函数( processSomething)!

使用GCD来实现单例模式

在 APP 的生命周期内你想确保每段代码只执行一次,即使它在代码的不同地方多次调用(比如单例的初始化)。 

直接代码:

static dispatch_once_t onceToken;

void (^executedOnlyOnce)(void) = ^{

    static NSUInteger numberOfEntries = 0;

    numberOfEntries++;

    NSLog(@"Executed %lu time(s)", (unsigned long)numberOfEntries);

};


调用:

dispatch_queue_t concurrentQueue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    

    dispatch_once(&onceToken, ^{

        dispatch_async(concurrentQueue,executedOnlyOnce);

    });


    

    dispatch_once(&onceToken, ^{

        dispatch_async(concurrentQueue,executedOnlyOnce);

    });


可以发现,输出只有一行,虽然我们调用了2次。因为我们传入的 dispatch_once_t是相同的。编译器只执行一次操作。

dispatch_once 方法


void dispatch_once(dispatch_once_t *predicate,dispatch_block_t block);


predicate:单例标识符

block:执行的Block 


在Application周期内,只执行一次Block。即:单例模式

dispatch_once_t:可以理解为单例标识符, dispatch_once方法用它来测试Block是否被执行过了。如果执行过了,那么就不在执行。

用 GCD 将任务分组     

有时候,我们可能执行一系列的任务。 由于彼此之间的依赖关系。比如有3个任务:A、B、C;我们必须执行了任务A,才能执行 任务B,最后执行任务C。这样的话,我们可以用GCD的分组机制来将多个任务来按照预定的顺序来执行。


先看例子吧:



我们先来搞清楚以下的关于调度组的一些常用方法

dispatch_group_t 方法


typedef struct dispatch_group_s *dispatch_group_t;


 

指代一个调度组。
调度组负责监视加入到该组的多个Block。每个Block可以是异步或者同步(取决与你自己)。
调度组中的Block可以在不同的队列中执行;并且一个Block可以被添加到多个调度组中。

dispatch_group_create 方法


dispatch_group_t dispatch_group_create(void);


创建一个调度组


dispatch_group_async 方法


void dispatch_group_async(dispatch_group_t group,dispatch_queue_t queue,dispatch_block_t block);


group:要分发到的调度组


queue:执行Block的队列


block:要执行的Block


将Block分发到指定的队列(用指定的队列来执行Block),并且将该Block加入到指定的调度组中。


dispatch_group_notify 方法


void dispatch_group_notify(dispatch_group_t group,dispatch_queue_t queue,dispatch_block_t block);


当调度组中的所有Block被执行完成后,将调用被分配到指定队列的Block。


dispatch_release 方法


void dispatch_release(dispatch_object_t object);


object:调度对象


销毁指定的调度组。


理论一大堆了,还是用事实来说话:

例子:

- (void)viewDidAppear:(BOOL)animated{

    

    [super viewDidAppear:animated];

    

    dispatch_group_t taskGroup = dispatch_group_create();// 创建一个调度组

    dispatch_queue_t mainQueue = dispatch_get_main_queue();// 创建队列

    

    // 任务1

    // Block添加到指定的调度组(taskGroup)中,并且该Block用指定的队列(mainQueue)执行。

    dispatch_group_async(taskGroup, mainQueue, ^{

        

        [self reloadTableView];

    });

    

    // 任务2

    // Block添加到指定的调度组(taskGroup)中,并且该Block用指定的队列(mainQueue)执行。

    dispatch_group_async(taskGroup, mainQueue, ^{

        

        [self reloadScrollView];

    });

    

    // 任务3

    // Block添加到指定的调度组(taskGroup)中,并且该Block用指定的队列(mainQueue)执行。

    dispatch_group_async(taskGroup, mainQueue, ^{

        

        [self reloadImageView];

    });

    

    // 当指定调度组(taskGroup)中的所有Block都执行完成后,将执行给定的Block,用指定的队列(mainQueue)。

    dispatch_group_notify(taskGroup, mainQueue, ^{

        // 指定的Block

        [[[UIAlertView alloc] initWithTitle:@"Finished"

                                    message:@"All tasks are finished" delegate:nil

                          cancelButtonTitle:@"OK" otherButtonTitles:nil, nil] show];

        

    });

    

    // 最后,必须release 掉调度组(taskGroup

    dispatch_release(taskGroup);


}  

#pragma mark - 执行的多个方法


- (void) reloadTableView{

    

    NSLog(@"%s"__FUNCTION__);

}


- (void) reloadScrollView{


    NSLog(@"%s"__FUNCTION__);

}


- (void) reloadImageView{


    NSLog(@"%s"__FUNCTION__);

    

}


执行结果:

2013-03-28 23:00:57.394 DemoVideo[25096:707] -[MoreViewController reloadTableView]

2013-03-28 23:00:57.395 DemoVideo[25096:707] -[MoreViewController reloadScrollView]

2013-03-28 23:00:57.396 DemoVideo[25096:707] -[MoreViewController reloadImageView]


可见,执行顺序,跟加入到调度组中的次序是一样的。

等等,有点疑惑是不是?!
我对每个任务用的是方法 dispatch_group_async,是异步的啊。为什么顺序却不变呢?!
呵呵,基础不扎实啊!因为他们都是分配给同一个队列 dispatch_get_main_queue() 中的!在一个队列中是串行的啊。所以,还是按照顺序来。

对,在GCD中,每个方法往往都有两种,一种是执行标准Block,一种是执行C函数的。

那么,就ok,我们来看看执行C函数的写法:

例子:

- (void)viewDidAppear:(BOOL)animated{

    

    [super viewDidAppear:animated];

    

    dispatch_group_t taskGroup = dispatch_group_create();

    dispatch_queue_t mainQueue = dispatch_get_main_queue();

    dispatch_group_async_f(taskGroup, mainQueue,(void *)selfreloadAllComponents);

    

    dispatch_group_notify(taskGroup, mainQueue, ^{

        

        [[[UIAlertView alloc] initWithTitle:@"Finished"

                                    message:@"All Tasks are Finished"

                                   delegate:nil

                          cancelButtonTitle:@"OK" otherButtonTitles:nilnil] show];

        });

    

    dispatch_release(taskGroup);

        


}


// 定义调度组要执行的C函数

void reloadAllComponents(void *context){

    MoreViewController *self =(MoreViewController *)context;

    [self reloadTableView];

    [self reloadScrollView];

    [self reloadImageView];

}


注意:
因为 dispatch_group_async_f不像 dispatch_group_t那样使用Block,可以访问当前类的变量。

由于  dispatch_group_async_f  接受  函数作为一个代码块来执行,所以,我们要执行 reloadTableView方法, reloadScrollView 方法reloadImageView方法,必须有个当前类的引用!!!!!
那么  C 函数必须有一 个引用到  Self,这个 Self 能够调用当前对象的实例方法 

用 GCD 创建自己的分发队列     

当然,GCD也给予了我们很多自主性质的操作。就是可以定义我们自己的分发队列。

有时候,突发奇想,自己定义一个队列,做自己的事情!那该多自由!这一定可以有的,哟,亲!!
爽啊!!!

注意:我们自定义的队列,往往不会在主队列中执行。而是单独分配一个线程来维护我们所定义的队列。

先来看代码吧:

- (void)viewDidAppear:(BOOL)animated{

    

    [super viewDidAppear:animated];

    

    // 创建指定的自定义的串行队列

    dispatch_queue_t firstSerialQueue =dispatch_queue_create("com.pixolity.GCD.serialQueue1", NULL);

    

    // 让队列异步执行Block

    dispatch_async(firstSerialQueue, ^{

        NSUInteger counter = 0;

        for (counter = 0; counter < 5; counter++){

            NSLog(@"First iteration, counter = %lu", (unsigned long)counter); }

            NSLog(@"Current thread = %@", [NSThread currentThread]);

    });

    

    dispatch_async(firstSerialQueue, ^{

        NSUInteger counter = 0for (counter = 0;counter < 5;counter++){

            NSLog(@"Second iteration, counter = %lu", (unsigned long)counter);

            NSLog(@"Current thread = %@", [NSThread currentThread]);

        }

    });

    

    dispatch_async(firstSerialQueue, ^{ NSUInteger counter = 0;

        for (counter = 0;counter < 5;counter++){

            NSLog(@"Third iteration, counter = %lu", (unsigned long)counter);

            NSLog(@"Current thread = %@", [NSThread currentThread]);

        }

    });

    

    // 销毁队列

    dispatch_release(firstSerialQueue);

    

    // 输出主队列,比较会发现,我们自定义的队列,并不在主线程上,效率还是蛮高的。

    dispatch_queue_t mainQueue1 = dispatch_get_main_queue();

    dispatch_async(mainQueue1, ^(void) {

        NSLog(@"Main thread = %@", [NSThread mainThread]);

    });

        


}


代码,不解释!

对,上面是Block形式的。还有一个C函数的自定义吧!

贴,贴更健康!

// 定义任务1-C函数形式

void firstIteration(void *paramContext){

    NSUInteger counter = 0;

    for (counter = 0;counter < 5;counter++){

        NSLog(@"First iteration, counter = %lu", (unsigned long)counter);

    }

}


// 定义任务2-C函数形式

void secondIteration(void *paramContext){

    NSUInteger counter = 0;

    for (counter = 0;counter < 5;counter++){

        NSLog(@"Second iteration, counter = %lu", (unsigned long)counter);

    }

}


// 定义任务3-C函数形式

void thirdIteration(void *paramContext){

    NSUInteger counter = 0;

    for (counter = 0;counter < 5;counter++){

        NSLog(@"Third iteration, counter = %lu", (unsigned long)counter);

    }

}


- (void)viewDidAppear:(BOOL)animated{

    

    [super viewDidAppear:animated];

    

    dispatch_queue_t firstSerialQueue =dispatch_queue_create("com.pixolity.GCD.serialQueue1", 0);

    dispatch_async_f(firstSerialQueue, NULLfirstIteration);

    dispatch_async_f(firstSerialQueue, NULLsecondIteration);

    dispatch_async_f(firstSerialQueue, NULLthirdIteration);

    dispatch_release(firstSerialQueue);

        


}


用 GCD   进行信号量的管理操作

dispatch_semaphore_create 方法

dispatch_semaphore_t dispatch_semaphore_create(long value);

创建一个信号量对象


value:信号量的值,必须大于等于0


什么是信号量?


我想举个例子你就会明白了。古时候执行某种命令需要令牌。创建一个信号量,就是创建一个命令。

dispatch_semaphore_t semaphore = dispatch_semaphore_create(3);

也就是说,一项任务semaphore,有3个令牌,就是说,最多可以同时分派3名不同的将领去执行。



dispatch_semaphore_wait

Waits for (decrements) a semaphore.

long dispatch_semaphore_wait(dispatch_semaphore_t dsema,dispatch_time_t timeout);

递减信号量。

dsema:信号量


timeout:等待信号量的策略

返回0,说明递减信号量成功。

也就是说,拿走一个令牌去执行某个任务。这时候,令牌数量会减去1。当令牌用完时,使用FIFO的原则进行等待。


dispatch_semaphore_signal


long dispatch_semaphore_signal(dispatch_semaphore_t dsema);

增加信号量

也就是说,某一个将领执行任务回来。并且将令牌上交。这样处于等待中的将领可以得到令牌并执行相应任务


例子:
使用信号量实现锁操作。

//主线程中

    TestObj *obj = [[TestObj allocinit];

    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

    

    //线程1

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        long thecount= dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

        NSLog(@"%ld",thecount);

        [obj method1];

        sleep(10);

        dispatch_semaphore_signal(semaphore);

    });

    

    //线程2

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        sleep(1);

        long thecount= dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

        NSLog(@"----%ld",thecount);


        [obj method2];

        dispatch_semaphore_signal(semaphore);

    });


这篇关于iOS GCD集汇(一)GCD(Grand Central Dispatch)和Block 使用-浅析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/813808

相关文章

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

安卓链接正常显示,ios#符被转义%23导致链接访问404

原因分析: url中含有特殊字符 中文未编码 都有可能导致URL转换失败,所以需要对url编码处理  如下: guard let allowUrl = webUrl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {return} 后面发现当url中有#号时,会被误伤转义为%23,导致链接无法访问

pdfmake生成pdf的使用

实际项目中有时会有根据填写的表单数据或者其他格式的数据,将数据自动填充到pdf文件中根据固定模板生成pdf文件的需求 文章目录 利用pdfmake生成pdf文件1.下载安装pdfmake第三方包2.封装生成pdf文件的共用配置3.生成pdf文件的文件模板内容4.调用方法生成pdf 利用pdfmake生成pdf文件 1.下载安装pdfmake第三方包 npm i pdfma

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

git使用的说明总结

Git使用说明 下载安装(下载地址) macOS: Git - Downloading macOS Windows: Git - Downloading Windows Linux/Unix: Git (git-scm.com) 创建新仓库 本地创建新仓库:创建新文件夹,进入文件夹目录,执行指令 git init ,用以创建新的git 克隆仓库 执行指令用以创建一个本地仓库的