iOS--锁的学习

iOS--锁的学习

  • 锁的介绍
    • 线程安全
  • 锁的分类
    • 自旋锁和互斥锁
    • OSSpinLock
    • os_unfair_lock
    • pthread_mutex
      • pthread_mutex的属性
    • NSLock
    • NSRecursiveLock
    • NSCondition
    • NSConditionLock
    • dispatch_semaphore
    • dispatch_queue
    • @synchronized
    • atomic
    • pthread_rwlock:读写锁
    • dispatch_barrier_async
  • 锁的性能比较

锁的介绍

主要参考了iOS多线程安全-13种线程锁🔒
在iOS开发中,锁是一种用于管理并发访问共享资源的机制。多个线程可以同时访问共享资源,但在某一时刻只允许一个线程对资源进行读取或修改,以避免数据竞争和不一致性。
锁的主要目的是确保在给定时间内只有一个线程可以进入被锁定的代码块或临界区。当一个线程获得了锁并进入临界区时,其他尝试获取锁的线程会被阻塞,直到该线程释放锁。这样可以保证在临界区内的代码只会被一个线程执行,从而避免竞态条件和数据不一致性的问题。

在iOS中,有多种锁机制可供选择,每种锁机制都有其特定的适用场景和性能特征。一些常见的锁机制包括:
互斥锁(Mutex):例如NSLock、pthread_mutex等。互斥锁提供了基本的线程同步机制,通过对临界区加锁和解锁来确保同一时间只有一个线程可以访问。
递归锁(Recursive Lock):例如NSRecursiveLock、pthread_mutex_recursive等。递归锁允许同一线程多次获取锁,避免了死锁情况。
条件锁(Condition Lock):例如NSConditionLock、pthread_cond等。条件锁在某些特定条件下才允许线程继续执行,否则线程会被阻塞。
信号量(Semaphore):例如dispatch_semaphore。信号量是一种更为通用的同步机制,可以用于控制多个线程的并发数量。
自旋锁(Spin Lock):例如os_unfair_lock、OSSpinLock(已被弃用)等。自旋锁在等待锁时不会将线程阻塞,而是通过循环忙等的方式尝试获取锁,适用于短时间内锁的竞争概率较低的情况。

线程安全

线程安全是指在多线程环境下,程序能够正确地处理共享数据并保证数据的一致性和正确性。
在多线程编程中,多个线程可以同时访问和修改共享的数据。如果没有适当的同步机制和线程安全的设计,可能会出现以下问题:

  • 竞态条件(Race Condition):多个线程同时对同一数据进行读写操作,导致结果依赖于线程执行的顺序,从而得到不确定的结果。
  • 数据竞争(Data Race):多个线程同时访问和修改共享数据,其中至少一个是写操作,导致未定义的行为。

这里用一个比较经典的例子,具体参考iOS多线程安全-13种线程锁🔒

//卖票演示
- (void)ticketTest{
self.ticketsCount = 50;
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);for (NSInteger i = 0; i < 5; i++) {
dispatch_async(queue, ^{
for (int i = 0; i < 10; i++) {
[self sellingTickets];
}
});
}}
//卖票
- (void)sellingTickets{
int oldMoney = self.ticketsCount;
sleep(.2);
oldMoney -= 1;
self.ticketsCount = oldMoney;NSLog(@"当前剩余票数-> %d", oldMoney);
}

运行结果如图:
在这里插入图片描述

这里是一个典型的数据竞争问题,当多个线程同时对一个共享数据进行读写操作是很有可能会发生数据竞争,为了避免这种情况,我们往往通过线程同步来保证线程安全。实现线程同步的方法很多,锁就是一种比较常见的用法 ;

锁的分类

锁是实现线程同步和保证数据访问的一种机制,在多线程编程中起着重要的作用。根据实现方式和特性,锁可以分为几个不同的分类:

  • 互斥锁(Mutex
    Lock):互斥锁是最常见和基本的锁类型,用于实现互斥访问共享资源。它提供了两个状态:锁定(被一个线程持有)和解锁(无线程持有)。只有一个线程可以持有互斥锁,其他线程在获取锁之前会被阻塞。互斥锁可以防止多个线程同时访问临界区,从而保证数据的一致性。
  • 读写锁(Read-Write
    Lock):读写锁也称为共享-独占锁,它允许多个线程同时读取共享资源,但只允许一个线程进行写操作。读操作之间不会互斥,而读操作与写操作之间是互斥的。读写锁适用于读多写少的场景,可以提高并发性能。
  • 自旋锁(Spin
    Lock):自旋锁是一种忙等待锁,它使用循环来反复检查锁是否被释放,而不是将线程阻塞。当一个线程发现自旋锁被其他线程持有时,它会一直循环等待,直到自旋锁可用。自旋锁适用于临界区很小且短时间内会释放的情况。
  • 条件变量(Condition
    Variable):条件变量用于线程之间的等待和通知机制。它与互斥锁结合使用,允许一个线程等待某个条件成立并在条件满足时通知其他线程。等待条件的线程会释放互斥锁,以便其他线程可以访问临界区。条件变量常用于生产者-消费者问题等场景。
  • 屏障(Barrier):屏障用于多个线程在某个点上等待,直到所有线程都到达该点后才继续执行。它可以用于同步多个线程的操作,确保线程在达到某个阶段之前都等待其他线程。

自旋锁和互斥锁

自旋锁(Spin Lock):
自旋锁是一种忙等待锁,它使用循环来反复检查锁的状态,直到获取到锁为止。自旋锁的特点如下:

  • 自旋锁适用于临界区很小且短时间内会释放的情况,因为它需要持续占用CPU资源来进行自旋等待。
  • 自旋锁不会导致线程的阻塞和切换,所以对于短时间内能够获取到锁的情况,自旋锁的性能较好。
  • 自旋锁不会改变线程的调度顺序,因此在高优先级线程和低优先级线程之间,可能会导致优先级反转的问题。

互斥锁是一种常见的线程同步机制,用于实现互斥访问共享资源。在iOS中,常用的互斥锁有NSLock和NSRecursiveLock。互斥锁的特点如下:

  • 互斥锁允许一个线程持有锁,其他线程在获取锁之前会被阻塞,从而实现对临界区的互斥访问。
  • 互斥锁会导致线程的阻塞和切换,所以对于长时间占用锁或等待时间不确定的情况,互斥锁的性能可能较差。
  • 互斥锁可以解决优先级反转的问题,因为它可以改变线程的调度顺序,让优先级高的线程优先获取锁。

互斥锁:线程会从sleep(加锁)——>running(解锁),过程中有上下文的切换,cpu的抢占,信号的发送等开销。
自旋锁:线程一直是running(加锁——>解锁),死循环检测锁的标志位,机制不复杂。
对比
互斥锁的起始原始开销要高于自旋锁,但是基本是一劳永逸,临界区持锁时间的大小并不会对互斥锁的开销造成影响,而自旋锁是死循环检测,加锁全程消耗cpu,起始开销虽然低于互斥锁,但是随着持锁时间,加锁的开销是线性增长。

OSSpinLock

OSSpinLock是iOS旧版本中提供的一种自旋锁(Spin Lock)实现。它通过忙等待的方式来获取锁,并且不会导致线程的阻塞和切换。不过需要注意的是,自旋锁在iOS 10之后已被标记为废弃,因为它存在优先级反转和性能问题。推荐使用更现代的互斥锁实现,如os_unfair_lock和pthread_mutex等。
使用时需要导入头文件#import <libkern/OSAtomic.h>
在这里插入图片描述

os_unfair_lock

os_unfair_lock是iOS 10及以上版本引入的一种互斥锁(Mutex Lock)实现,用于实现线程同步和保护共享资源的访问。相比于旧版本中的自旋锁(OSSpinLock),os_unfair_lock采用了更高级的算法来解决优先级反转和性能问题。
os_unfair_lock的特点如下:

  • os_unfair_lock是一种互斥锁,只能由一个线程持有,其他线程在获取锁之前会被阻塞。
  • 它可以解决优先级反转问题,确保高优先级线程在等待锁时能够优先获取。
  • os_unfair_lock的实现采用了更高级的算法,避免了自旋等待,减少了不必要的CPU开销。
  • os_unfair_lock的性能通常比旧版自旋锁(OSSpinLock)更好,尤其在高并发情况下。
    需要导入头文件#import <os/lock.h>
@implementation ViewController {os_unfair_lock _lock ;
}- (void)viewDidLoad {[super viewDidLoad];[self ticketTest] ;}//卖票演示
- (void)ticketTest{
self.ticketsCount = 50;
//os_unfair_lock lock = OS_UNFAIR_LOCK_INIT ;_lock = OS_UNFAIR_LOCK_INIT ;dispatch_queue_t queue = dispatch_get_global_queue(0, 0);for (NSInteger i = 0; i < 5; i++) {dispatch_async(queue, ^{for (int i = 0; i < 10; i++) {[self sellingTickets];}});}}
//卖票
- (void)sellingTickets{os_unfair_lock_lock(&_lock) ;int oldMoney = self.ticketsCount;sleep(.2);oldMoney -= 1;self.ticketsCount = oldMoney;NSLog(@"当前剩余票数-> %d", oldMoney);os_unfair_lock_unlock(&_lock) ;
}@end

在这里插入图片描述

要注意一下,通常不建议将 os_unfair_lock 直接作为属性来使用。os_unfair_lock 是一个结构体,不具备 Objective-C 对象的特性,因此不适合直接作为属性。

pthread_mutex

pthread_mutex 是 POSIX 线程库中提供的一种互斥锁(mutex)类型,用于实现线程同步和互斥访问共享资源。
主要的 pthread_mutex 函数如下:

  • pthread_mutex_init:用于初始化互斥锁,并指定互斥锁的属性。可以使用 NULL 作为第二个参数来使用默认属性。
  • pthread_mutex_destroy:用于销毁互斥锁。
  • pthread_mutex_lock:加锁操作,将互斥锁加锁,如果互斥锁已经被其他线程锁定,则当前线程被阻塞直到互斥锁可用。
  • pthread_mutex_trylock:尝试加锁操作,如果互斥锁已经被其他线程锁定,则该操作会立即返回,而不会阻塞当前线程。
  • pthread_mutex_unlock:解锁操作,释放互斥锁,允许其他线程获得锁。
@implementation ViewController {
//    os_unfair_lock _lock ;pthread_mutex_t _lock ;
}- (void)viewDidLoad {[super viewDidLoad];[self ticketTest] ;}//卖票演示
- (void)ticketTest{
self.ticketsCount = 50;
//os_unfair_lock lock = OS_UNFAIR_LOCK_INIT ;
//初始化锁属性pthread_mutexattr_t attr ;pthread_mutexattr_init(&attr) ;pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) ;pthread_mutex_init(&_lock, &attr) ;//第二个参数可以为NULL,表示使用默认属性PTHREAD_MUTEX_NORMALdispatch_queue_t queue = dispatch_get_global_queue(0, 0);for (NSInteger i = 0; i < 5; i++) {dispatch_async(queue, ^{for (int i = 0; i < 10; i++) {[self sellingTickets];}});}}
//卖票
- (void)sellingTickets{
//    os_unfair_lock_lock(&_lock) ;pthread_mutex_lock(&_lock) ;int oldMoney = self.ticketsCount;sleep(.2);oldMoney -= 1;self.ticketsCount = oldMoney;NSLog(@"当前剩余票数-> %d", oldMoney);
//    os_unfair_lock_unlock(&_lock) ;pthread_mutex_unlock(&_lock) ;
}@end

在这里插入图片描述

pthread_mutex的属性

pthread_mutexattr_settype:设置互斥锁的类型。常见选项有:

  • PTHREAD_MUTEX_NORMAL:普通互斥锁,不支持递归。
  • PTHREAD_MUTEX_RECURSIVE:递归互斥锁,允许同一线程多次获得锁。
  • PTHREAD_MUTEX_ERRORCHECK:错误检查互斥锁,会检测多次锁定同一互斥锁的错误。
  • PTHREAD_MUTEX_DEFAULT:根据实现的默认类型设置互斥锁。

NSLock

**NSLock是对mutex普通锁的封装。**pthread_mutex_init(mutex, NULL);
NSLock 遵循 NSLocking 协议。Lock 方法是加锁,unlock 是解锁,tryLock 是尝试加锁,如果失败的话返回 NO,lockBeforeDate: 是在指定Date之前尝试加锁,如果在指定时间之前都不能加锁,则返回NO

@protocol NSLocking
- (void)lock;
- (void)unlock;
@end@interface NSLock : NSObject <NSLocking> {
@private
void *_priv;
}- (BOOL)tryLock;
- (BOOL)lockBeforeDate:(NSDate *)limit;
@property (nullable, copy) NSString *name
@end
@implementation ViewController {
//    os_unfair_lock _lock ;
//    pthread_mutex_t _lock ;}- (void)viewDidLoad {[super viewDidLoad];[self ticketTest] ;}//卖票演示
- (void)ticketTest{
self.ticketsCount = 50;
//os_unfair_lock lock = OS_UNFAIR_LOCK_INIT ;
//初始化锁属性
//    pthread_mutexattr_t attr ;
//    pthread_mutexattr_init(&attr) ;
//    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) ;
//    pthread_mutex_init(&_lock, &attr) ;//第二个参数可以为NULL,表示使用默认属性PTHREAD_MUTEX_NORMALself.lock = [[NSLock alloc] init] ;dispatch_queue_t queue = dispatch_get_global_queue(0, 0);for (NSInteger i = 0; i < 5; i++) {dispatch_async(queue, ^{for (int i = 0; i < 10; i++) {[self sellingTickets];}});}}
//卖票
- (void)sellingTickets{
//    os_unfair_lock_lock(&_lock) ;
//    pthread_mutex_lock(&_lock) ;[self.lock lock] ;int oldMoney = self.ticketsCount;sleep(.2);oldMoney -= 1;self.ticketsCount = oldMoney;NSLog(@"当前剩余票数-> %d", oldMoney);
//    os_unfair_lock_unlock(&_lock) ;
//    pthread_mutex_unlock(&_lock) ;[self.lock unlock] ;
}//- (instancetype)init {
//    self = [super init];
//    if (self) {
//        _lock = [[NSLock alloc] init];
//    }
//    return self;
//}//- (void)lock {
//    [_lock lock];
//}
//
//- (void)unlock {
//    [_lock unlock];
//}@end

在这里插入图片描述

NSRecursiveLock

NSRecursiveLock是对mutex递归锁的封装,API跟NSLock基本一致

- (void)ticketTest{
self.ticketsCount = 50;
//os_unfair_lock lock = OS_UNFAIR_LOCK_INIT ;
//初始化锁属性
//    pthread_mutexattr_t attr ;
//    pthread_mutexattr_init(&attr) ;
//    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) ;
//    pthread_mutex_init(&_lock, &attr) ;//第二个参数可以为NULL,表示使用默认属性PTHREAD_MUTEX_NORMALself.lock = [[NSRecursiveLock alloc] init] ;dispatch_queue_t queue = dispatch_get_global_queue(0, 0);for (NSInteger i = 0; i < 5; i++) {dispatch_async(queue, ^{for (int i = 0; i < 10; i++) {[self sellingTickets];}});}}
//卖票
- (void)sellingTickets{
//    os_unfair_lock_lock(&_lock) ;
//    pthread_mutex_lock(&_lock) ;[self.lock lock] ;int oldMoney = self.ticketsCount;sleep(.2);oldMoney -= 1;self.ticketsCount = oldMoney;NSLog(@"当前剩余票数-> %d", oldMoney);
//    os_unfair_lock_unlock(&_lock) ;
//    pthread_mutex_unlock(&_lock) ;[self.lock unlock] ;
}

NSCondition

NSCondition是对mutex和cond的封装,更加面向对象 ;

@interface NSCondition : NSObject <NSLocking>- (void)wait;
- (BOOL)waitUntilDate:(NSDate *)limit;
- (void)signal;
- (void)broadcast;@property (nullable, copy) NSString *name;@end
  • NSCondition 类继承自 NSObject 并遵循 NSLocking 协议。
  • wait 方法用于使当前线程等待,直到接收到信号。 waitUntilDate: 方法使当前线程等待,直到接收到信号或者指定的日期超时。
  • signal 方法用于唤醒一个等待中的线程。
  • broadcast 方法用于唤醒所有等待中的线程。
  • name 属性是一个可选的字符串,用于标识 NSCondition 对象的名称。
// 线程1
// 删除数组中的元素
- (void)__remove
{
[self.condition lock];
if (self.data.count == 0) {
// 等待
[self.condition wait];
}
[self.data removeLastObject];
NSLog(@"删除了元素");
[self.condition unlock];
}// 线程2
// 往数组中添加元素
- (void)__add
{
[self.condition lock];
sleep(1);
[self.data addObject:@"Test"];
NSLog(@"添加了元素");
// 信号
[self.condition signal];
[self.condition unlock];
}

我的想法是这类对象有两种线程等待的方式,一种是wait–signal方式,一种是lock–unlock方式 ;

NSConditionLock

NSConditionLock是对NSCondition的进一步封装,可以设置具体的条件值

@interface NSConditionLock : NSObject <NSLocking> {- (instancetype)initWithCondition:(NSInteger)condition;@property (readonly) NSInteger condition;
- (void)lockWhenCondition:(NSInteger)condition;
- (BOOL)tryLock;
- (BOOL)tryLockWhenCondition:(NSInteger)condition;
- (void)unlockWithCondition:(NSInteger)condition;
- (BOOL)lockBeforeDate:(NSDate *)limit;
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;
@property (nullable, copy) NSString *name;
@end

dispatch_semaphore

ispatch_semaphore 是 Grand Central Dispatch (GCD) 中的一个信号量机制,用于控制并发访问共享资源的线程数量。这个之前GCD中讲过了 ;

dispatch_queue

使用GCD的串行队列也可以实现线程同步的

@synchronized

@synchronized 是 Objective-C 中用于实现线程安全的关键字之一。它提供了一种简单的方式来保护共享资源,以防止多个线程同时访问和修改该资源。
使用 @synchronized 的基本语法如下:

@synchronized (object) {// 同步的代码块// ...
}

@synchronized 关键字的工作原理是,在进入 @synchronized 代码块之前,它会获取 object 的互斥锁,以确保同一时间只有一个线程可以进入该代码块。当线程离开 @synchronized 代码块时,它会释放锁,允许其他线程继续进入。

关于@synchronized 的底层原理可以去深入看看,但平时使用只要知道它会在代码块开头加锁,代码块末尾解锁就行了

atomic

在Objective-C中,atomic是一种属性修饰符,用于指定属性的原子性。当一个属性被声明为atomic时,它意味着在对该属性进行读取和写入操作时,将会保证操作的原子性。
原子性是指一个操作要么完全执行,要么完全不执行,不存在执行过程中被中断的情况。在多线程环境下,使用atomic修饰符可以确保对属性的读取和写入操作是线程安全的。
当属性被声明为atomic时,编译器会自动生成相关的同步代码,以确保对属性的访问是原子操作。这样可以防止多个线程同时对同一个属性进行读写操作,避免出现数据竞争和不一致的情况。
需要注意的是,虽然atomic提供了一定程度的线程安全性,但它并不能完全保证线程安全。在高并发的多线程环境中,仍然需要额外的同步机制来确保数据的一致性和正确性。
相比之下,另一个属性修饰符nonatomic则表示属性是非原子的,它不会提供自动的线程安全保护。在多线程环境下,使用nonatomic修饰符可以提高性能,但需要开发人员自行处理线程安全问题。
总之,atomic属性修饰符用于指定属性的原子性,提供一定程度的线程安全性。在多线程环境中,可以使用atomic来确保对属性的读取和写入操作是原子的,但仍需要注意其他的线程安全问题。

pthread_rwlock:读写锁

//初始化锁
pthread_rwlock_t lock;
pthread_rwlock_init(&_lock, NULL);//读加锁
pthread_rwlock_rdlock(&_lock);
//读尝试加锁
pthread_rwlock_trywrlock(&_lock)//写加锁
pthread_rwlock_wrlock(&_lock);
//写尝试加锁
pthread_rwlock_trywrlock(&_lock)//解锁
pthread_rwlock_unlock(&_lock);
//销毁
pthread_rwlock_destroy(&_lock);

pthread_rwlock 是 POSIX 线程库中的读写锁(read-write lock)机制。它提供了一种多读单写的并发访问控制机制,允许多个线程同时读取共享资源,但只允许一个线程进行写操作。
使用 pthread_rwlock 可以实现以下功能:

  • 多读单写控制:多个线程可以同时获取读锁(读取共享资源),但只有一个线程可以获取写锁(修改共享资源)。
  • 读写互斥:在写锁被持有期间,其他线程无法获取读锁,以确保数据一致性。

dispatch_barrier_async

GCD中也有 ;

锁的性能比较

性能从高到低排序

1、os_unfair_lock
2、OSSpinLock
3、dispatch_semaphore
4、pthread_mutex
5、dispatch_queue(DISPATCH_QUEUE_SERIAL)
6、NSLock
7、NSCondition
8、pthread_mutex(recursive)
9、NSRecursiveLock
10、NSConditionLock
11、@synchronized

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

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

相关文章

摸鱼大数据——Hive基础理论知识——Hive基础架构

1、Hive和MapReduce的关系 1- 用户在Hive上编写数据分析的SQL语句&#xff0c;然后再通过Hive将SQL语句翻译成MapReduce程序代码&#xff0c;最后提交到Yarn集群上进行运行 2- 大家可以将Hive理解成有道词典&#xff0c;帮助你翻译英文 2、Hive架构 用户接口: 包括 CLI、JDBC/…

Java+Swing+Mysql实现飞机订票系统

一、系统介绍 1.开发环境 操作系统&#xff1a;Win10 开发工具 &#xff1a;Eclipse2021 JDK版本&#xff1a;jdk1.8 数据库&#xff1a;Mysql8.0 2.技术选型 JavaSwingMysql 3.功能模块 4.数据库设计 1.用户表&#xff08;users&#xff09; 字段名称 类型 记录内容…

脑机接口习题

9-12章习题 填空题 EEG电极分为 主动电极 和 被动电极 &#xff0c;其中 被动电极 直接与放大器连接&#xff0c; 主动电极 包含一个1~10倍的前置放大。除抗混淆滤波器&#xff0c;放大系统也包含由电阻器、电容器构成的模拟滤波器&#xff0c;把信号频率内容限制在一个特定的…

B树与B+树区别

B树和B树是常见的数据库索引结构&#xff0c;都具有相较于二叉树层级较少&#xff0c;查找效率高的特点&#xff0c;它们之间有以下几个主要区别&#xff1a; 1.节点存储数据的方式不同 B树的叶子结点和非叶子节点都会存储数据&#xff0c;指针和数据共同保存在同一节点中B树…

当标签中出现输入了字母或者数字直接在一行上,没有换行的 情况时怎么办

当标签块中输入的是包含字母或者数字的时候&#xff0c;他不会换行&#xff0c;在一行上显示滚动条的形式&#xff0c;而我们想让他走正常文档流&#xff0c;该换行的时候换行 想要的如下效果 给相应的元素块添加该代码即可 word-break: break-all; .card-content { …

酷开科技大屏营销,多元需求唤醒“客厅经济”

随着科技的发展和消费者习惯的变化&#xff0c;OTT大屏营销正逐渐成为客厅经济的新风向。OTT不仅改变了人们获取信息和娱乐的方式&#xff0c;也为品牌营销提供了新的机遇和挑战&#xff0c;OTT大屏营销已经成为客厅经济的重要组成部分。酷开科技通过其自主研发的智能电视操作系…

一文了解 - GPS/DR组合定位技术

GPS Global Position System 全球定位系统这个大家都很熟悉&#xff0c; 不做太多介绍。 DR Dead Reckoning 车辆推算定位法&#xff0c; 一种常用的辅助的车辆定位技术。 DR系统的优点&#xff1a; 不需要发射和接收信号&#xff1b; 不受电磁波干扰。 DR系统的缺点&#x…

项目管理-质量管理

目录 一、质量管理概述 1.1 GB/T16260.1-2006 定义 1.2 GB/T19000-ISO 9000(2000)系列标准定义 二、软件质量模型 2.1 软件全生命周期质量模型 2.1.1 内部和外部质量的质量模型 2.1.2 使用质量的质量模型 2.1.3 McCall 质量模型 2.1.4 质量特性度量 2.1.5 相关概念 三…

【全开源】多功能投票小程序(ThinkPHP+FastAdmin+Uniapp)

打造高效、便捷的投票体验 一、引言 在数字化快速发展的今天&#xff0c;投票作为一种常见的决策方式&#xff0c;其便捷性和效率性显得尤为重要。为了满足不同场景下的投票需求&#xff0c;我们推出了这款多功能投票小程序系统源码。该系统源码设计灵活、功能丰富&#xff0…

spark实战:实现分区内求最大值,分区间求和以及获取日志文件固定日期的请求路径

spark实战&#xff1a;实现分区内求最大值&#xff0c;分区间求和以及获取日志文件固定日期的请求路径 Apache Spark是一个广泛使用的开源大数据处理框架&#xff0c;以其快速、易用和灵活的特点而受到开发者的青睐。在本文中&#xff0c;我们将通过两个具体的编程任务来展示S…

罗德里格斯公式(旋转矩阵)推导

文章目录 1. 推导2. 性质3. 参考 1. 推导 r r r为旋转轴&#xff0c; θ \theta θ为旋转角度。 先将旋转轴单位化 u r ∣ ∣ r ∣ ∣ u\frac{r}{||r||} u∣∣r∣∣r​ 旋转可以被分为垂直和旋转两个方向&#xff0c; 我们求沿轴方向的分量其实就是在求 p p p向量在 u u u方…

将本地项目上传到 gitee 仓库

1、创建 gitee 仓库 到 gitee 官网&#xff0c;新建仓库 配置新建仓库 完成仓库的创建 项目上传到仓库 上传项目需要安装git git官方下载地址&#xff1a;git下载地址 安装完成&#xff0c;前往本地项目所在文件夹&#xff0c;右击选择 Git Bash Here 刚下载完成需要配置G…

Python筑基之旅-运算符

目录 一、运算符 1、了解定义 2、理解意义 2-1、基本数据处理 2-2、条件判断 2-3、逻辑操作 2-4、赋值和更新 2-5、位操作 2-6、提高代码可读性 2-7、解决实际问题 2-8、学习其他编程语言的基础 3、探索方法 3-1、理解概念 3-2、练习基本运算 3-3、掌握优先级 …

WIFI国家码设置的影响

记录下工作中关于国家码设置对WIFI的影响&#xff0c;以SKYLAB的SKW99和SDZ202模组为例进行说明。对应到日常&#xff0c;就是我们经常提及手机是“美版”“港版”等&#xff0c;它们的wifi国家码是不同的&#xff0c;各版本在wifi使用中遇到的各种情况与下面所述是吻合的。 现…

信息系统项目管理师0131:输出(8项目整合管理—8.7监控项目工作—8.7.3输出)

点击查看专栏目录 文章目录 8.7.3 输出8.7.3 输出 工作绩效报告工作绩效信息可以用实体或电子形式加以合并、记录和分发。基于工作绩效信息,以实体或电子形式编制形成工作绩效报告,以制定决策、采取行动或引起关注。根据项目沟通管理计划,通过沟通过程向项目干系人发送工作绩…

车道线识别与预警系统LDWS(代码+教程)

车道线识别与预警系统&#xff08;Lane Departure Warning System, LDWS&#xff09;作为智能交通系统中的重要组成部分&#xff0c;旨在通过先进的图像处理和计算机视觉技术&#xff0c;实时监测车辆行驶过程中的车道位置&#xff0c;预防因驾驶员疏忽或疲劳导致的车道偏离事故…

网络工程师---第三十八天

ISIS&#xff1a; ISIS含义&#xff1a;中间系统到中间系统IS-IS。 ISIS特点&#xff1a;①内部网关协议IGP&#xff08;Interior Gateway Protocol&#xff09;&#xff0c;用于自治系统内部&#xff1b; ②IS-IS也是一种链路状态协议&#xff0c;使用最短路径优先SPF算法进…

【Linux】为 VMware 的 Linux 系统(CentOS 7)设置静态IP地址

文章目录 准备工作查看 子网掩码 和 网关IP确认准备设置的虚拟机端口没有被占用 调整设置编辑配置文件配置文件说明 完成配置&#xff0c;准备测试使用命令终端连接服务器 我是一名立志把细节说清楚的博主&#xff0c;欢迎【关注】&#x1f389; ~ 原创不易&#xff0c; 如果有…

一个人应该怎么操作抖音小店呢?店铺操作流程给你讲解清楚!

大家好&#xff0c;我是电商小V 现在入驻抖音小店的有很多新手&#xff0c;新手最关心的就是一个人应该如何操作抖音小店&#xff0c;操作抖音小店需要做好哪几步呢&#xff1f;关于这个问题咱们就来详细的讲解一下&#xff0c; 第一点&#xff1a;开店 开店是做店的第一步&…