【iOS】——GCD总结

news2024/9/22 19:43:22

同步和异步的区别

同步执行等待操作完成,而异步执行允许程序在操作完成前继续运行,提高了效率和响应性。这里的关键就是上一个操作需不需要等待当前操作的执行,如果需要就是同步,如果不需要就是异步。

异步有开启新线程的能力但不一定开启,同步没有开启新线程的能力。

两者的区别在于是否等待队列中的任务执行结束,是否具备开启新线程的能力

什么情况下使用串行队列,什么情况下使用并发队列

  • 串行队列(Serial Dispatch Queue):等待现在执行中的处理,也就是每次只有一个任务被执行。让任务一个接着一个地执行。(只开启一个线程,一个任务执行完毕后,再执行下一个任务)
  • 并发队列(Concurrent Dispatch Queue):不等待现在执行中的处理,也就是可以让多个任务并发(同时)执行。(可以开启多个线程,并且同时执行任务)

并发功能只有在异步(dispatch_async)函数下才有效

串行队列用于确保任务按顺序执行,适用于任务间有依赖关系或需要独占访问共享资源的场景,而并发队列则允许多个任务同时执行,适合于能够并行处理的任务以提高性能和效率,如独立的计算或数据处理任务

GCD和NSOperation队列的区别

  • GCD中的队列有串行队列和并发队列,都是采用FIFO原则,也就是创建的新任务总是在队尾,执行的当前任务总是从队头开始。添加到队列中的任务不能取消或暂停。

  • NSOperationQueue有主队列和自定义队列。自定义队列有串行和并发两种,通过设置操作队列的最大并发操作数来实现。添加到队列中的操作可以取消或者暂停。

主队列是什么

主队列其实就是串行队列,只是在默认情况下当前代码放在主队列中,然后主队列中的代码,又都会放到主线程中去执行。

主队列的任务一定在主线程中进行,主线程中进行的任务不一定是主队列的任务还可能是其它队列的任务。

GCD的优先级

GCD中有四个优先级:分别是高优先级(High Priority)、默认优先级(Default Priority)、低优先级(Low Priority)和后台优先级(Background Priority)

如何理解GCD中的同步和异步操作

GCD中的同步操作:是指将block块中的任务添加到队列的队尾时需要等待队头的任务在线程上执行完并且移除后才能添加,这里边有一个等待队头任务完成的过程

GCD中的异步操作:是指将block块中的任务添加到队列的队尾时直接将所有任务全部依次添加到队尾,不用管队头的任务有没有在线程上执行完并移除,这里边就涉及到了不需要等待的意思。

如何理解GCD中的串行队列和并发队列

GCD中的串行队列和并发队列都遵循FIFO(先进先出)原则,也就是说先添加到队列中的任务先出去,后添加到队列中的任务后出去。

串行队列:

串行队列保证队列中的任务会按照它们被添加到队列中的顺序依次执行(先添加的任务会先开始执行,后添加的任务必须等待前面的任务完成才能开始)。

如何保证的呢?

当一个任务被添加到串行队列中时,无论是同步还是异步添加,GCD 都会将这个任务放入队列的末尾无非是要不要等待的过程。串行队列的调度器会从队列的头部取出任务放到线程上来执行,无论开启多少的新线程,它限制每次只执行一个任务,一旦当前任务完成,才会从队列中取出下一个任务开始执行

并发队列

并发队列有让队列中的任务在多个线程上同时进行的能力,这取决于是否开启新线程。

  • 如果当前只开启了一个线程那么队列中的任务还是需要等待队头的任务在线程上执行完才能执行后面的任务,也就跟串行队列是一样的。

  • 如果当前开启了多个线程比如说三个线程,那么队列会分配三个任务到这三个线程上同时执行,哪个线程先执行完就将队头的任务添加到哪个线程上。

如何理解任务的执行顺序

任务的执行顺序是任务被添加到队列中的顺序。如果是顺序执行的话就是任务按照队列的FIFO原则,先进的先执行,后进的后执行。如果是乱序的话就不是先进的先执行,后进的后执行。造成乱序的原因就是多线程中的并发执行。

队列+任务 组合方式

1.串行队列+同步执行

同步执行是是指将block块中的任务添加到队列的队尾时需要等待队头的任务在线程上执行完并且移除后才能添加。串行队列限制每次只执行一个任务,一旦当前任务完成,才会从队列中取出下一个任务开始执行。因此任务是顺序执行的。

2.串行队列+异步执行

异步执行是将所有任务依次添加到队列中,不会等待任务完成。并发队列有让队列中的任务在多个线程上同时进行的能力,并且在异步执行会开启新线程。所以任务是乱序执行的

3.并发队列+同步执行

同步执行时将block块中的任务添加到队列的队尾时需要等待队头的任务在线程上执行完并且移除后才能添加。并发队列有让队列中的任务在多个线程上同时进行的能力。所以这里关键要看是否开启新线程。因为是同步执行所以没有开启新线程,所以按顺序执行。

如果在同步执行之前,在该并发队列中异步执行了添加任务的操作的话,就会开启新线程。此时再同步执行的话,虽然添加任务这个操作需要等待线程上的任务执行完才能进行,但此时已经有多个线程,这样就不能保证任务的顺序。此时同步和异步执行的任务都是乱序的。

如果是同步执行之后再进行异步操作,虽然开启了新线程,但是同步执行的任务在开启新线程之前就已经全部执行完了,这个新线程只负责处理异步操作中的任务,也就是同步执行的还是顺序,异步执行的是乱序。

因此下面两段代码的运行结果是有区别的,第一段代码是不按照顺序执行,第二段代码按照顺序执行

dispatch_queue_t concurrentQueue = dispatch_queue_create("com.example.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);

    dispatch_async(concurrentQueue, ^{
        NSLog(@"Task 1 started");
        sleep(0);
        NSLog(@"Task 1 completed");
    });

    dispatch_sync(concurrentQueue, ^{
        NSLog(@"Task 2 started");
        sleep(2);
        NSLog(@"Task 2 completed");
    });

    dispatch_sync(concurrentQueue, ^{
        NSLog(@"Task 3 started");
        sleep(2);
        NSLog(@"Task 3 completed");
    });

在这里插入图片描述

dispatch_sync(concurrentQueue, ^{
        NSLog(@"Task 1 started");
        sleep(0);
        NSLog(@"Task 1 completed");
    });

    dispatch_sync(concurrentQueue, ^{
        NSLog(@"Task 2 started");
        sleep(2);
        NSLog(@"Task 2 completed");
    });

    dispatch_async(concurrentQueue, ^{
        NSLog(@"Task 3 started");
        sleep(2);
        NSLog(@"Task 3 completed");
    });

在这里插入图片描述

4.并发队列+异步执行

异步执行是将所有任务依次添加到队列中,不会等待任务完成。并发队列有让队列中的任务在多个线程上同时进行的能力,并且在异步执行会开启新线程。所以任务是乱序执行的

5.主队列+同步执行

首先需要知道的是主队列的任务一定是在主线程上进行的。同步执行需要等待线程上的任务执行完之后才能将任务添加到队尾。将任务同步添加到队尾这个操作其实也是个任务,而这个任务一般是在主线程上进行。此时就形成了主队列中的任务需要等待将任务添加到队列的这个操作的任务执行完才能进行,而将任务添加到队列这个操作需要等待主线程上的任务执行完才能进行,形成了主队列和主线程相互等待的局面,造成了死锁。

如果是在 其他线程 调用 主队列+同步执行,则不会阻塞 主队列,也就不会造成死锁

6.主队列+异步执行

主队列的任务在主线程上进行。异步执行是不需要等待线程上的任务执行完就直接添加。当异步添加到队列的任务在主线程上执行完后就会执行队列里的任务。因此也不会造成死锁。因为主队列的本质是个串行队列,所以还是按顺序执行。

GCD的API

dispatch_set_target_queue(设置目标队列)

第一个参数为要变更优先级的队列,第二个参数为指定的队列所属的优先级(也就是第一个队列变换的优先级)

dispatch_set_target_queue(changedQueue, targetQueue);

还可以作为Dispatch Queue的执行阶层:如果在多个串行队列中用dispatch_set_target_queue函数指定目标为某一个串行队列,那么原本要并发执行的多个串行队列现在只能在指定的串行队列上处理,也就变成非并发处理。

dispatch_after(设置延迟执行)

实现在指定时间(例如 1 秒)之后执行某个任务,需要注意的是dispatch_after 方法并不是在指定时间之后才开始执行处理,而是在指定时间之后将任务追加到主队列中。

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        // 2.0 秒后异步追加任务代码到主队列,并开始执行
        NSLog(@"after---%@",[NSThread currentThread]);  // 打印当前线程
    });
Dispatch Group(队列组)

当我们想分别并发执行多个耗时任务,然后这几个耗时任务都执行完毕后再回到主线程执行任务。这时候我们可以用到 GCD 的队列组

先创建队列和队列组,将多个任务放到队列中再把队列放到队列组中,最后调用 dispatch_group_notify 回到指定线程执行任务。或者使用 dispatch_group_wait
回到当前线程继续向下执行(会阻塞当前线程)。

- (void)useDispatchGroup {
  //创建队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  //创建队列组
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, queue, ^{NSLog(@"blk0");});
    dispatch_group_async(group, queue, ^{NSLog(@"blk1");});
    dispatch_group_async(group, queue, ^{NSLog(@"blk2");});
    dispatch_group_notify(group, queue, ^{NSLog(@"Done!");});
}

dispatch_group_notify

监听 group 中任务的完成状态,当所有的任务都执行完成后,追加任务到 group 中,并执行任务。

也就是当所有任务都执行完成之后,才执行 dispatch_group_notify 相关 block 中的任务。
第一个参数为队列组,第二个参数是要追加执行的任务

 dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"add task");
    });
dispatch_group_wait

暂停当前线程(阻塞当前线程),等待指定的 group 中的任务执行完成后,才会往下继续执行。

dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

dispatch_group_enter、dispatch_group_leave

dispatch_group_enter 标志着一个任务追加到 group,执行一次,相当于 group 中未执行完毕任务数 +1
dispatch_group_leave 标志着一个任务离开了 group,执行一次,相当于 group 中未执行完毕任务数 -1。

当 group 中未执行完毕任务数为0的时候,才会使 dispatch_group_wait 解除阻塞,以及执行追加到 dispatch_group_notify 中的任务。

dispatch_barrier_async(栅栏方法)

主要用来将并发队列中的多个任务分割成两组来执行,只有当第一组的任务执行完并且执行了dispatch_barrier_async中追加的任务后才能执行第二组的任务。

这个函数传入的并发队列必须是自己通过dispatch_queue_cretate创建的 如果传入的是一个串行或是一个全局的并发队列,那这个函数便等同于dispatch_async函数的效果

dispatch_once(限制一次方法)

使用 dispatch_once 方法能保证某段代码在程序运行过程中只被执行 1 次,并且即使在多线程的环境下,dispatch_once 也可以保证线程安全。

/**
 * 一次性代码(只执行一次)dispatch_once
 */
- (void)once {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // 只执行 1 次的代码(这里面默认是线程安全的)
    });
}

dispatch_apply(快速迭代方法)

按照指定的次数将指定的任务追加到指定的队列中,快速迭代一组任务,并等待全部队列执行结束。主要用于需要快速并行处理大量相同类型任务的场景,如图像处理、数据处理或任何形式的批处理任务。

在串行队列中无法体现出它的作用,因为队列中的任务始终是一次执行一个。

在并发队列中如果开启了多个线程的话就能在多个线程中同时展开执行,效率特别高。

void dispatch_apply(
    size_t iterations,
    dispatch_queue_t queue,
    dispatch_apply_iterate_t iterator
);
  • iterations 是你要执行的任务数量。
  • queue 是你希望在其中执行任务的队列,通常是并发队列。
  • iterator 是一个指向函数的指针,这个函数将为每个迭代执行一次。

用下面这段代码举个例子:

for (int i = 0; i < 100000; i++) {
     NSString *queueN = [NSString stringWithFormat:@"c%dqueue", i];
     dispatch_queue_t cQueue = dispatch_queue_create(queueN.UTF8String, DISPATCH_QUEUE_CONCURRENT);
     dispatch_async(cQueue, ^{
            NSLog(@"the queue = %s", dispatch_queue_get_label(cQueue));
        });
}

在上面代码中模拟了我们想要在多线程循环执行任务场景,但是突然创建很多线程或造成线程爆炸,可以使用dispatch_apply来实现,因为dispatch_apply的线程全部由GCD管理。从而避免了手动创建线程爆炸问题。

// 解决线程爆炸方案
dispatch_queue_t queue = dispatch_queue_create("xxQueue", DISPATCH_QUEUE_CONCURRENT);

dispatch_apply(100000, queue, ^(size_t t) {
        NSLog(@"the current thread = %@, t = %ld", NSThread.currentThread, t);
});

dispatch_suspend & dispatch_resume(暂停和恢复)

dispatch_suspend函数可以随时暂停某个队列,等需要执行的时候使用dispatch_resume恢复即可。

 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_suspend(queue);
    dispatch_resume(queue);

dispatch_semaphore(信号量)

  • emaphore叫做”信号量”
  • 信号量的初始值,可以用来控制线程并发访问的最大数量
  • 信号量的初始值为1,代表同时只允许1条线程访问资源,保证线程同步
//表示最多访问5个线程
dispatch_semaphore_create(5);
// 如果信号量的值 > 0,就让信号量的值减1,然后继续往下执行代码
// 如果信号量的值 <= 0,就会休眠等待,直到信号量的值变成>0,就让信号量的值减1,然后继续往下执行代码
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
// 让信号量的值+1
dispatch_semaphore_signal(self.semaphore);

主要控制的是能够访问共享资源或执行临界区的线程数量,而不是直接控制线程开启的数量。

dispatch_semaphore_t semaphore = dispatch_semaphore_create(1); // 创建一个初始值为1的信号量

dispatch_queue_t concurrentQueue = dispatch_queue_create("com.example.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(concurrentQueue, ^{
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); // 等待信号量,获得资源访问权限
    NSLog(@"Task 1: Accessing shared resource");
    // 执行对共享资源的访问
    dispatch_semaphore_signal(semaphore); // 释放资源访问权限
});

dispatch_async(concurrentQueue, ^{
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); // 等待信号量,获得资源访问权限
    NSLog(@"Task 2: Accessing shared resource");
    // 执行对共享资源的访问
    dispatch_semaphore_signal(semaphore); // 释放资源访问权限
});

// 为了确保主线程等待所有任务完成,可以使用 dispatch_sync 或 dispatch_group
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
dispatch_async(concurrentQueue, ^{
    dispatch_group_leave(group);
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

什么场景下会出现死锁?

有三种场景会出现死锁:

  • 主队列+同步执行
  • 异步串行队列嵌套同步串行队列
  • 信号量阻塞主线程
场景一:同步+主队列

[self task1];
dispatch_sync(dispatch_get_main_queue(), ^{
	[self task2];
    NSLog(@"同步线程执行主队列任务");
});
[self task3];

(1)执行task1
(2)阻塞同步线程,把task2加入到主队列的队尾
(3)task3需要等待task2执行完成后执行,但是此时task2又排在task3后面,所以造成了死锁

场景二:异步串行队列嵌套同步串行队列

dispatch_queue_t myQueue = dispatch_queue_create("com.bg.sQueue", DISPATCH_QUEUE_SERIAL);
[self task1];
dispatch_async(myQueue, ^{
   [self task2];
   dispatch_sync(myQueue, ^{
   [self task3];
   });
   [self task4];
});
[self task5];

(1)执行task1
(2)执行task5
(3)执行task2
(4)阻塞同步线程,把task3加入到队列myQueue的队尾
(5)task4需要等待task3执行完成后执行,但是此时task3又排在task4后面,所以造成了死锁

场景三:信号量阻塞主线程

dispatch_semaphore_t sem = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_main_queue(), ^{
    dispatch_semaphore_signal(sem);
    NSLog(@"the sem +1");
});
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
NSLog(@"the sem -1");

主线程中dispatch_semaphore_wait一直等着dispatch_semaphore_signal改变信号量(+1操作),但是dispatch_semaphore_wait却阻塞了主线程导致dispatch_semaphore_signal无法执行,从而造成了死锁。

GCD的任务能取消吗?

GCD本身没有提供取消任务的API,但是可以通过操作来间接设置取消任务比如:

  • 在 block 内部添加一个布尔变量作为取消标志,然后在外部改变这个标志的值。block 在执行时可以检查这个标志是否被设置为取消状态,如果被设置,则提前结束 block 的执行。

  • DispatchSource 可以用来监控文件描述符、定时器等事件,但也可以用于模拟任务取消。可以创建一个 DispatchSourceTimer,并在定时器触发时检查是否应该取消任务。

如何用GCD实现任务间的依赖?

实现任务间的依赖通常涉及到确保一个任务在另一个任务完成之后才开始执行。

  • 因此可以使用串行队列来解决。因为串行队列保证任务按顺序执行。当你向串行队列中添加任务时,GCD 会确保前一个任务完成之后才开始执行下一个任务

  • 在并发队列中,还可以使用dispatch_barrier_async 来确保在执行屏障任务之前,所有之前提交到队列的任务都已经完成。这可以用来实现任务间的依赖。

如何使用GCD来实现控制最大并发数?

可以使用**dispatch_semaphore**

首先,创建一个信号量,其初始值等于你想要的最大并发数。每当开始一个新任务时,尝试减少信号量的值。如果信号量的值为0,新的任务将被阻塞,直到有任务完成并释放信号量。

如何实现一个常驻线程?(线程保活)

  • 使用无限循环

在一个线程中使用无限循环(如 while 循环)来持续执行任务或监听事件。为了防止线程占用过多的 CPU 资源,可以在循环中加入适当的延时。

  • 使用 dispatch_source_t

dispatch_source_t 是 GCD 中一种可以用于监听事件的对象,比如定时器、文件描述符等。可以创建一个 dispatch_source_t 定时器,让它定期触发并执行任务。

  • 使用NSThread

使用NSThread创建线程并在其中执行无限循环或监听事件。

怎样利用GCD实现多读单写呢(怎么实现一个多读单写的模型)?

通过栅栏异步调用的方式,分配写操作到并发队列当中。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1976873.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何构建AI产品:OpenAI与前Shopify产品负责人Miqdad Jaffer的经验分享

一、引言 构建AI产品是一项复杂且充满挑战的任务&#xff0c;尤其是当涉及到面向消费者的解决方案时。在最近的一期播客节目中&#xff0c;OpenAI 和前Shopify产品负责人 Miqdad Jaffer 分享了他在构建AI产品的经验和策略。下面我们将探讨构建AI产品的最佳实践&#xff0c;以及…

行为型设计模式1:状态/策略/命令

行为型设计模式&#xff1a;状态/策略/命令 (qq.com)

【秋招笔试】24-08-03-米哈游-秋招提前批笔试题

🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 💻 ACM金牌团队🏅️ | 多次AK大厂笔试 | 编程一对一辅导 ✨ 本系列打算持续跟新 秋招笔试题 👏 感谢大家的订阅➕ 和 喜欢💗 ✨ 笔试合集传送们 -> 🧷春秋招笔试合集 🍰 米哈游提前批笔试也是来了,本次题目…

初谈表的约束

文章目录 概念空属性默认值空属性和默认值对比列描述zerofill主键 概念 真正约束字段的是数据类型&#xff0c;但是数据类型约束很单一&#xff0c;需要有一些额外的约束&#xff0c;更好的保证数据的合法性&#xff0c;从业务逻辑角度保证数据的正确性。比如有一个字段是emai…

Open3D 计算点云的归一化协方差矩阵

目录 一、概述 1.1原理 1.2实现步骤 1.3应用 二、代码实现 2.1关键函数 2.2完整代码 三、实现效果 3.1原始点云 3.2数据显示 Open3D点云算法汇总及实战案例汇总的目录地址&#xff1a; Open3D点云算法与点云深度学习案例汇总&#xff08;长期更新&#xff09;-CSDN博…

文章相关接口

1.新增文章分类 文章分类的表结构和实体类 实体类 接口文档 实现 新创建CategoryController,CategoryService,(CategoryServiceImpl),CategoryMapper 在CategoryController中添加方法 使用注解PostMapping,没有映射路径&#xff0c;我们在CategoryController的类上添加一个映…

Java 并发编程:Java 中的乐观锁与 CAS

大家好&#xff0c;我是栗筝i&#xff0c;这篇文章是我的 “栗筝i 的 Java 技术栈” 专栏的第 025 篇文章&#xff0c;在 “栗筝i 的 Java 技术栈” 这个专栏中我会持续为大家更新 Java 技术相关全套技术栈内容。专栏的主要目标是已经有一定 Java 开发经验&#xff0c;并希望进…

【DOCKER】显示带UI的软件

1. Linux 1.1 宿主机开放X server权限 xhost 1.2 启动容器 docker run -it --rm --privilegedtrue --useru20 --workdir/home/u20 \ -e DISPLAYhost.docker.internal:0 u20:dev1.3 测试 # 安装测试软件 sudo apt-get -y install x11-apps# 显示测试程序 xclock2. Windows …

websocket的学习

第一步&#xff1a;配置Spring <dependency><groupId>org.springframework</groupId><artifactId>spring-messaging</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> …

RabbitMQ知识总结(基本原理+高级特性)

文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 基本原理 消息的可靠性投递 RabbitMQ 消息的投递路径为&#xff…

Idea包含UI内容的插件开发

Idea包含UI内容的插件开发 前言插件效果项目结构配置功能的实现找一个股票接口完成最终的页面配置Plugin.xml源码地址 前言 在这一篇文章中将会做一个包含UI内容的能看股票的插件。 插件效果 首先是在设置中配置股票的编号&#xff0c;如sh000001,sh600519。 接着在侧边栏中…

手机端微信聊天记录无法全部同步到电脑端的微信?搞定它!

前言 昨天晚上深夜…… 哼哼&#xff0c;想哪去了&#xff1f; 昨天有个深圳的哥们跟小白吐槽&#xff1a;手机端的微信聊天记录怎么没办法自动同步到电脑端上&#xff1f; 刚开始小白还以为他是因为电脑端的微信在线也没办法同步聊天记录&#xff0c;所以就给出了答案&…

样式与特效(3)——实现一个测算页面

这次我们使用前端实现一个简单的游戏页面,理论上可以增加很多玩法&#xff0c;&#xff0c;但是这里为了加深前端的样式和JS点击事件&#xff0c;用该案例做练习。 首先需要掌握手机端的自适应&#xff0c;我们是只做手机端玩家页面 。需要允许自适应手机端页面&#xff0c; 用…

OpenCV||超详细的图像处理模块

一、颜色变换cvtColor dst cv2.cvtColor(src, code[, dstCn[, dst]]) src: 输入图像&#xff0c;即要进行颜色空间转换的原始图像。code: 转换代码&#xff0c;指定要执行的颜色空间转换类型。这是一个必需的参数&#xff0c;决定了源颜色空间到目标颜色空间的转换方式。dst…

实现元素定位:掌握Selenium八大定位方法

文章目录 0. 八大定位方法1. id2. name3. xpath4. css_selector 0. 八大定位方法 当实现测试自动化&#xff0c;编写测试用例时&#xff0c;首先需要在web界面找到对应元素位置&#xff0c;而Selenium提供了一套对应的API&#xff0c;被封装在WebDriver类中。如下图&#xff0…

JAVA字符串刷题(力扣经典算法及题解)

练习一&#xff1a; 输入字符串aba,依次输出各个字符 import java.util.Scanner;public class StringTomrs {public static void main(String[] args) {Scanner scnew Scanner(System.in);String numbersc.next();System.out.println("输入的字符串是"number);for(i…

使用FastChat快速部署LLM服务

原文&#xff1a;https://zhuanlan.zhihu.com/p/705915093 FastChat 是一个用于训练、服务和评估基于LLM的聊天机器人的开放平台&#xff0c;它的核心功能包括&#xff1a; 最先进模型&#xff08;例如 Vicuna、MT-Bench&#xff09;的训练和评估代码。具有 Web UI 和与 Open…

<Rust>使用rust实现crc16_modbus校验码生成?

前言 本文是使用rust代码来实现crc16 modbus校验码的输出。 概述 crc16 modbus算法简介: 代码实现: crc16 modbus是crc校验码的其中一种计算方式,通常用于modbus类通讯的数据校验上。 其计算步骤如上面的图片所示,通常此校验算法用在工控行业比较多,如一些支持串口通讯…

Linux驱动----总线

总线相关 总线注册和注销总线device对象----描述设备信息&#xff0c;包括地址&#xff0c;中断号和其他的一些自定义数据注册和注销device对象----指将device注册到mybus总线 driver对象----描述设备驱动的方法&#xff08;操作地址和中断&#xff09;注册和注销driver对象---…

38 器件移动、旋转、镜像、对齐、等间距操作介绍39 器件、网络、过孔锁定与解锁操作40 相同模块复用操作41 测量、查询功能介绍

38 器件移动、旋转、镜像、对齐、等间距操作介绍&&39 器件、网络、过孔锁定与解锁操作&&40 相同模块复用操作&& 41 测量、查询功能介绍 第一部分 38 器件移动、旋转、镜像、对齐、等间距操作介绍第二部分 39 器件、网络、过孔锁定与解锁操作第三部分 4…