当前位置: 首页 > news >正文

iOS底层原理系列04-并发编程

在移动应用开发中,流畅的用户体验至关重要,而并发编程是实现这一目标的关键技术。本文将深入探讨iOS平台上的并发编程和多线程架构,帮助你构建高性能、响应迅速的应用程序。

1. iOS线程调度机制

1.1 线程本质和iOS线程调度机制

线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。一个进程可以拥有多个线程,每个线程共享进程的资源,但拥有自己的执行路径。

在iOS系统中,线程调度由系统内核负责,通过优先级队列和时间片轮转算法确定线程的执行顺序和执行时长。苹果对标准的线程模型进行了优化,引入了更高级的抽象,如GCD和Operation,使开发者能够专注于任务本身,而非线程管理的细节。

1.2 并发计算模型中的同步/异步与串行/并行

并发编程中的两组基本概念:同步/异步和串行/并行。这两组概念看似简单,但常被混淆,它们实际上描述了两个不同的维度。

同步/异步的本质

同步(Synchronous)与异步(Asynchronous)描述的是调用方式,关注的是线程的等待方式

  • 同步调用:调用者会一直等待被调用的任务完成后,才继续执行后续代码。调用者线程在任务执行期间处于阻塞状态。
  • 异步调用:调用者不会等待被调用的任务完成,会立即继续执行后续代码。任务的完成通常通过回调、通知或其他机制通知调用者。

串行/并行的本质

**串行(Serial)与并行(Concurrent)**描述的是任务的执行方式,关注的是多个任务之间的关系。

  • 串行执行:多个任务按顺序依次执行,任何时刻只有一个任务在执行。
  • 并行执行:多个任务可以同时执行,任务之间相互独立,各自在不同的线程上执行。

同步/异步与串行/并行排列组合的调度机制和执行效果

这两组概念可以组合出四种不同的调度情况,下面我们详细分析每种组合的调度机制和执行效果。

在这里插入图片描述

1. 同步 + 串行

  • 调度机制:任务在当前线程上按顺序执行
  • 执行效果:调用者线程被阻塞,直到所有任务完成

2. 同步 + 并行

  • 调度机制:任务被分配到多个线程并发执行
  • 执行效果:调用者线程仍然被阻塞,直到所有任务完成
  • 注意:这种组合在实际中较少使用,因为即使任务并行执行,调用者仍需等待所有任务完成,无法充分利用并行的优势

3. 异步 + 串行

  • 调度机制:任务在另一个线程上按顺序执行
  • 执行效果:调用者线程立即返回继续执行后续代码,不会被阻塞

4. 异步 + 并行

  • 调度机制:任务被分配到多个线程并发执行
  • 执行效果:调用者线程立即返回继续执行后续代码,不会被阻塞,同时多个任务可以同时执行,充分利用多核处理器性能

这四种组合方式构成了iOS多线程编程的基础模型,也是理解GCD和NSOperation等高级API的关键。

2. iOS线程方案

iOS提供了多种多线程编程方案,从底层的pthread到高级的GCD和NSOperation,为开发者提供了灵活的选择。

2.1 pthread

pthread(POSIX thread)是一套跨平台的线程API标准,iOS也支持这一标准。它比NSThread更底层,提供了更多的控制选项。

#import <pthread.h>// 创建线程
pthread_t thread;
pthread_create(&thread, NULL, ThreadFunction, NULL);// 线程函数
void *ThreadFunction(void *data) {// 执行任务NSLog(@"任务在pthread中执行");return NULL;
}// 等待线程结束
pthread_join(thread, NULL);

优点

  • 跨平台兼容性好
  • 控制粒度更细
  • 可以设置线程的各种属性

缺点

  • API复杂,使用不便
  • 需要手动管理线程的各个方面
  • 缺乏Objective-C与iOS集成的便利特性

2.2 NSThread

NSThread是Objective-C中最基本的线程类,它是对pthread的面向对象封装,提供了创建和管理线程的基本功能。

// 创建并启动线程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(doSomething:) object:nil];
thread.name = @"MyCustomThread";
[thread start];// 或者使用类方法创建并自动启动线程
[NSThread detachNewThreadSelector:@selector(doSomething:) toTarget:self withObject:nil];

优点

  • 简单直观,面向对象的API
  • 可以直接控制线程的生命周期

缺点

  • 需要手动管理线程生命周期
  • 缺乏高级特性,如线程池、任务依赖等
  • 线程创建和销毁的开销较大

2.3 GCD

GCD是一个强大的并发编程框架,通过任务和队列的概念简化了多线程编程。GCD的核心思想是让开发者关注"做什么"而不是"怎么做"。
在这里插入图片描述

2.3.1 GCD的核心概念:

队列(Queue):负责存储和管理任务。

  • 串行队列(Serial Queue):按顺序执行任务。
  • 并行队列(Concurrent Queue):可以同时执行多个任务。
  • 主队列(Main Queue):在主线程上执行任务,通常用于UI更新。

任务(Task):以Block(代码块)形式提交到队列。

调度方式

  • 同步调度(sync):等待任务完成后返回。
  • 异步调度(async):提交任务后立即返回。

下面是GCD的常见用法示例:

// 获取全局并行队列
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);// 异步执行任务
dispatch_async(globalQueue, ^{// 耗时操作NSData *data = [self fetchDataFromServer];// 在主队列更新UIdispatch_async(dispatch_get_main_queue(), ^{[self updateUIWithData:data];});
});// 创建自定义串行队列
dispatch_queue_t serialQueue = dispatch_queue_create("com.example.serialQueue", DISPATCH_QUEUE_SERIAL);// 同步执行任务
dispatch_sync(serialQueue, ^{// 这会阻塞当前线程直到该任务完成[self processData];
});

2.3.2 GCD其他高级功能

如分组(group)、信号量(semaphore)、一次性执行(once)等:

// 使用组管理多个任务
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);// 添加任务到组
dispatch_group_async(group, queue, ^{// 任务1
});
dispatch_group_async(group, queue, ^{// 任务2
});// 等待所有任务完成
dispatch_group_notify(group, dispatch_get_main_queue(), ^{NSLog(@"所有任务已完成");
});

优点

  • 简洁高效的API
  • 自动管理线程池
  • 针对多核处理器优化
  • 低系统开销

缺点

  • 相比NSOperation,不支持取消任务
  • 不支持任务优先级(旧版本)
  • 调试难度相对较高

2.4 NSOperation/NSOperationQueue

NSOperationNSOperationQueue是基于GCD构建的更高级的抽象,提供了面向对象的API和更强大的任务管理能力。

NSOperation是一个抽象类,开发者通常使用其子类:

  1. NSBlockOperation:用于执行一个或多个Block的操作。
  2. NSInvocationOperation:用于调用特定对象的选择器。
  3. 自定义NSOperation子类:实现复杂任务逻辑。

NSOperationQueue用于管理和执行NSOperation对象:

// 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 4; // 设置最大并发数// 创建操作
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{NSData *imageData = [self downloadImageData];UIImage *image = [UIImage imageWithData:imageData];// 在主队列更新UI[[NSOperationQueue mainQueue] addOperationWithBlock:^{self.imageView.image = image;}];
}];// 添加完成回调
operation1.completionBlock = ^{NSLog(@"图片下载完成");
};// 添加操作到队列
[queue addOperation:operation1];

NSOperation/NSOperationQueue最强大的特性是可以设置操作之间的依赖关系,构建复杂的工作流:

// 创建多个操作
NSBlockOperation *downloadOp = [NSBlockOperation blockOperationWithBlock:^{// 下载图片
}];NSBlockOperation *filterOp = [NSBlockOperation blockOperationWithBlock:^{// 过滤图片
}];NSBlockOperation *saveOp = [NSBlockOperation blockOperationWithBlock:^{// 保存图片
}];// 设置依赖关系
[filterOp addDependency:downloadOp]; // 先下载,再过滤
[saveOp addDependency:filterOp];     // 先过滤,再保存// 添加到队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperations:@[downloadOp, filterOp, saveOp] waitUntilFinished:NO];

NSOperation还支持任务的取消、暂停和恢复:

// 取消单个操作
[operation cancel];// 取消队列中所有操作
[queue cancelAllOperations];// 暂停队列
queue.suspended = YES;// 恢复队列
queue.suspended = NO;

优点

  • 面向对象的API
  • 支持操作的取消、暂停和恢复
  • 支持操作优先级
  • 支持操作间依赖关系
  • 内置了完成块(completionBlock)
  • KVO兼容,可以观察操作状态

缺点

  • 相比GCD,开销略大
  • API相对复杂
  • 初始化和配置需要更多代码

3. iOS中的线程安全方案

多线程编程中,一个关键问题是如何确保共享资源的访问安全。iOS提供了多种锁机制和同步方案,下面按性能从高到低介绍。

3.1 os_unfair_lock

这是iOS 10引入的锁机制,用于替代已废弃的OSSpinLock。它是一种低级互斥锁,性能极高。

#import <os/lock.h>// 创建锁
os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;// 加锁
os_unfair_lock_lock(&lock);
// 临界区代码
os_unfair_lock_unlock(&lock);// 尝试加锁(非阻塞)
if (os_unfair_lock_trylock(&lock)) {// 加锁成功,执行临界区代码os_unfair_lock_unlock(&lock);
} else {// 加锁失败
}

适用场景:需要高性能且临界区操作简短的场景。

3.2 OSSpinLock (已废弃)

自旋锁在等待锁释放时会持续尝试获取锁,不会进入休眠状态。虽然性能很高,但在iOS平台上存在优先级反转问题,已被苹果废弃。

#import <libkern/OSAtomic.h>// 创建锁
OSSpinLock lock = OS_SPINLOCK_INIT;// 加锁
OSSpinLockLock(&lock);
// 临界区代码
OSSpinLockUnlock(&lock);

注意:由于存在优先级反转问题,不推荐使用,应改用os_unfair_lock。

3.3 dispatch_semaphore_t

信号量是一种计数器,可以用来控制访问共享资源的线程数量。

// 创建信号量,初始值为1(表示互斥锁)
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);// 等待(减1,如果结果小于0则等待)
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// 临界区代码
// 释放(加1,如果之前小于0则唤醒等待线程)
dispatch_semaphore_signal(semaphore);

适用场景:需要控制并发访问数量的场景,不仅限于互斥访问。

3.4 pthread_mutex

POSIX线程库提供的互斥锁,是一种通用的同步机制。

pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);// 加锁
pthread_mutex_lock(&mutex);
// 临界区代码
pthread_mutex_unlock(&mutex);// 释放锁
pthread_mutex_destroy(&mutex);

适用场景:需要可靠互斥且对性能要求不极端高的通用场景。

3.5 dispatch_queue(DISPATCH_QUEUE_SERIAL)

串行队列可以用作同步机制,确保任务按顺序执行。

// 创建串行队列
dispatch_queue_t queue = dispatch_queue_create("com.example.safeQueue", DISPATCH_QUEUE_SERIAL);// 同步执行(类似于加锁)
dispatch_sync(queue, ^{// 临界区代码
});// 异步执行(非阻塞)
dispatch_async(queue, ^{// 临界区代码
});

适用场景:适合需要异步执行且保证顺序的场景,更偏向任务编排而非简单锁定。

3.6 NSLock

Foundation框架提供的Objective-C互斥锁类。

NSLock *lock = [[NSLock alloc] init];// 加锁
[lock lock];
// 临界区代码
[lock unlock];// 尝试加锁(非阻塞)
if ([lock tryLock]) {// 加锁成功[lock unlock];
}// 带超时的加锁
if ([lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:1.0]]) {// 在1秒内获取到锁[lock unlock];
}

适用场景:需要面向对象API和超时功能的一般场景。

3.7 NSCondition

条件变量和互斥锁的结合,用于线程间的等待和通知机制。

NSCondition *condition = [[NSCondition alloc] init];// 生产者线程
[condition lock];
// 修改共享数据
[condition signal]; // 或 [condition broadcast]
[condition unlock];// 消费者线程
[condition lock];
while (/* 条件不满足 */) {[condition wait]; // 等待通知
}
// 临界区代码
[condition unlock];

适用场景:生产者-消费者模式等需要线程间通信的场景。

3.8 NSRecursiveLock

递归锁允许同一线程多次获取锁,而不会导致死锁。

NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];// 第一次加锁
[lock lock];
// 可以再次获取同一把锁而不会死锁
[lock lock];
// 临界区代码
[lock unlock];
[lock unlock]; // 需要平衡调用unlock

适用场景:需要在递归调用或嵌套调用中使用锁的场景。

3.9 @synchronized

Objective-C提供的语言级同步原语,使用简单但性能相对较低。

@synchronized(self) {// 临界区代码
}

适用场景:对性能要求不高的简单同步场景,或原型开发。

3.10 原子属性 (atomic) 实现原理

Objective-C中的属性可以声明为atomic,保证读写操作的原子性:

@property (atomic, strong) NSString *name;

实现原理:编译器会为atomic属性生成访问器方法,使用自旋锁/互斥锁确保读写操作的原子性。

限制:atomic只保证单个属性的读写原子性,不保证相关操作的原子性。例如,对数组的原子性读写不保证数组内容的访问也是原子的。

3.11 读写安全方案

除了互斥锁外,还有专门针对读多写少场景优化的读写锁:

pthread_rwlock

读写锁允许多个线程同时读取共享资源,但写操作需要独占访问。

pthread_rwlock_t rwlock;
pthread_rwlock_init(&rwlock, NULL);// 读锁(共享)
pthread_rwlock_rdlock(&rwlock);
// 读取操作
pthread_rwlock_unlock(&rwlock);// 写锁(独占)
pthread_rwlock_wrlock(&rwlock);
// 写入操作
pthread_rwlock_unlock(&rwlock);// 销毁锁
pthread_rwlock_destroy(&rwlock);
dispatch_barrier_async:异步栅栏调用

GCD提供的栅栏函数可以用于实现高效的读写分离:

// 创建并发队列
dispatch_queue_t queue = dispatch_queue_create("com.example.rwQueue", DISPATCH_QUEUE_CONCURRENT);// 读操作(多个可并发执行)
dispatch_async(queue, ^{// 读取操作
});// 写操作(栅栏函数,保证独占访问)
dispatch_barrier_async(queue, ^{// 写入操作
});

栅栏函数的工作原理是:

  1. 等待队列中已有的任务完成
  2. 独占式执行栅栏任务
  3. 栅栏任务完成后,后续的普通任务才能执行

这种方式非常适合读多写少的场景,能够提供极高的并发性能。

4. 常见陷阱

4.1 死锁情况及预防

死锁发生在两个或多个线程互相等待对方释放锁的情况。最常见的死锁场景:

// 主线程
dispatch_sync(dispatch_get_main_queue(), ^{// 这会导致死锁,因为主线程尝试同步等待主队列的任务,// 而主队列的任务必须等待主线程完成当前执行
});

预防措施

  1. 避免在持有锁时获取另一个锁
  2. 如需多个锁,按固定顺序获取
  3. 使用带超时的锁获取方式
  4. 避免在主线程上同步派发到主队列
  5. 使用GCD的dispatch_group或信号量来协调多个异步操作

4.2 优先级反转

当低优先级线程持有锁,高优先级线程等待该锁,而中优先级线程占用CPU时,高优先级线程会被无限期阻塞。

解决方案

  1. 使用优先级继承的锁机制
  2. 避免在临界区执行耗时操作
  3. 使用合适的队列优先级

4.3 主线程阻塞引起的UI卡顿

在主线程上执行耗时操作会导致UI无法响应,造成卡顿感:

// 错误示例: 主线程同步等待耗时操作
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"https://example.com/large-file.zip"]];

正确做法

  1. 将耗时操作移至后台线程
  2. 使用异步API而非同步API
  3. 合理分解大任务为小任务
  4. 使用Instruments等工具监测和优化主线程性能
// 正确示例: 异步执行耗时操作,完成后回到主线程更新UI
dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"https://example.com/large-file.zip"]];dispatch_async(dispatch_get_main_queue(), ^{// 更新UI[self.imageView setImage:[UIImage imageWithData:data]];});
});

总结

iOS提供了多种并发编程和线程安全的机制,从底层的pthread到高级的GCD和NSOperation。选择合适的机制需要考虑以下因素:

  1. 性能需求:对于性能要求极高的场景,考虑os_unfair_lock或dispatch_semaphore;对于一般场景,NSLock和串行队列足够。
  2. 编程范式:如果偏好面向对象的API,选择NSOperation和NSLock系列;如果偏好函数式编程,选择GCD。
  3. 任务特性:如果需要复杂的任务依赖和取消机制,选择NSOperation;如果是简单的并发任务,GCD更简洁。
  4. 同步需求:读多写少场景推荐读写锁或栅栏函数;需要线程通信的场景适合条件变量。

相关文章:

iOS底层原理系列04-并发编程

在移动应用开发中&#xff0c;流畅的用户体验至关重要&#xff0c;而并发编程是实现这一目标的关键技术。本文将深入探讨iOS平台上的并发编程和多线程架构&#xff0c;帮助你构建高性能、响应迅速的应用程序。 1. iOS线程调度机制 1.1 线程本质和iOS线程调度机制 线程是操作…...

企业数字化转型数据治理解决方案(119页PPT)(文末有下载方式)

资料解读&#xff1a;企业数字化转型数据治理解决方案 详细资料请看本解读文章的最后内容。 在当今数字化时代&#xff0c;数据已经成为企业最宝贵的资产之一。然而&#xff0c;随着数据量的激增和数据来源的多样化&#xff0c;如何有效管理和利用这些数据成为了企业面临的一…...

git报错:“fatal:refusing to merge unrelated histories“

新建仓库&#xff0c;克隆本地项目到新仓库&#xff0c;首次同步本地已提交的代码到远程时&#xff0c;报错&#xff1a;"fatal:refusing to merge unrelated histories" 。 报错意思是&#xff1a;致命的&#xff1a;拒绝合并无关的历史。 一、问题背景&#xff…...

Jmeter下载及环境配置

Jmeter下载及环境配置 java环境变量配置配置jdk环境变量检查是否配置成功JMeter下载 java环境变量配置 访问地址&#xff1a; https://www.oracle.com/cn/java/technologies/downloads/ 注意&#xff1a;需要自己注册账号 下载完成&#xff0c;解压后的目录为&#xff1a; …...

K8S学习之基础二十四:k8s的持久化存储之pv和pvc

K8S的存储之pv和pvc 在 Kubernetes (k8s) 中&#xff0c;持久化存储是通过 PersistentVolume (PV) 和 PersistentVolumeClaim (PVC) 来实现的。PVC 是用户对存储资源的请求&#xff0c;而 PV 是集群中的实际存储资源。PVC 和 PV 的关系类似于 Pod 和 Node 的关系。 Persisten…...

1.5、Java构造方法重载

构造方法重载的实现 &#xff08;1&#xff09;定义多个构造方法 class Person {private String name;private int age;// 无参构造方法public Person() {this.name "Unknown";this.age 0;}// 带一个参数的构造方法public Person(String name) {this.name name;…...

领域驱动设计(DDD)技术分享:从三层架构到DDD的进化之旅

一、开篇话&#xff1a;我们为什么要聊DDD&#xff1f; 如果你像我一样有着Java开发背景&#xff0c;那Spring的三层架构可能是你的老朋友了。Controller-Service-DAO这种模式简直就像我们编程的"家常便饭"。但是&#xff0c;随着业务越来越复杂&#xff0c;你是否也…...

LeetCode - #227 基于 Swift 实现基本计算器

摘要 在这篇文章中&#xff0c;我们将实现一个基于 Swift 语言的基本计算器。该计算器能够解析和计算包含 、-、* 和 / 的数学表达式&#xff0c;并且遵循运算符的优先级规则。整数除法仅保留整数部分&#xff0c;不能使用 eval() 这样的内置解析方法。 描述 给你一个字符串表…...

Elasticsearch Java High Level Client [7.17] 使用

es 的 HighLevelClient存在es源代码的引用&#xff0c;结合springboot使用时&#xff0c;会存在es版本的冲突&#xff0c;这里记录下解决冲突和使用方式&#xff08;es已经不建议使用这个了&#xff09;。 注意es服务端的版本需要与client的版本对齐&#xff0c;否则返回数据可…...

[多线程]基于环形队列(RingQueue)的生产者-消费者模型的实现

标题&#xff1a;[多线程]基于环形队列&#xff08;RingQueue&#xff09;的生产者-消费者模型 水墨不写bug 一、模型实现 接下来我们要实现一个基于环形队列&#xff08;RingQueue&#xff09;的生产者-消费者模型。该模型使用信号量和互斥锁来保证生产者和消费者之间的同步与…...

HAL库STM32常用外设—— CAN通信(一)

文章目录 一、CAN是什么&#xff1f;1.1 CAN应用场景1.2 CAN通信优势 二、CAN基础知识介绍2.1 CAN总线结构2.2 CAN总线特点2.2.1 CAN总线的数据传输特点2.2.2 位时序和波特率 2.3 CAN位时序和波特率2.3 CAN物理层2.3.1 CAN 物理层特性2.3.2 CAN 收发器芯片介绍 2.4 CAN协议层2.…...

分页查询的实现

目录 前言 一.问题描述 二.后端实现步骤 2.1配置PageHelper插件 ①导入依赖 ②在application.yml配置文件中添加相关配置 2.2编写一个入门的程序&#xff0c;体验分页过程 2.3定义一个vo&#xff0c;用来收集分页后的所有信息 2.4修改serviceImpl层的代码 2.5动态设…...

Sourcetree——使用.gitignore忽略文件或者文件夹

一、为何需要文件忽略机制&#xff1f; 1.1 为什么要会略&#xff1f; 对于开发者而言&#xff0c;明智地选择忽略某些文件类型&#xff0c;能带来三大核心优势&#xff1a; 仓库纯净性&#xff1a;避免二进制文件、编译产物等污染代码库 安全防护&#xff1a;防止敏感信息&…...

Thinkphp的belongsToMany(多对多) 和 hasManyThrough(远程一对多)的区别是什么?

虽然 belongsToMany&#xff08;多对多&#xff09; 和 hasManyThrough&#xff08;远程一对多&#xff09; 都会使用 JOIN 查询&#xff0c;但它们的核心区别在于 关联关系的本质不同&#xff0c;具体如下&#xff1a; 1️⃣ belongsToMany&#xff08;多对多&#xff09; &a…...

DataWhale 大语言模型 - 大模型技术基础

本课程围绕中国人民大学高瓴人工智能学院赵鑫教授团队出品的《大语言模型》书籍展开&#xff0c;覆盖大语言模型训练与使用的全流程&#xff0c;从预训练到微调与对齐&#xff0c;从使用技术到评测应用&#xff0c;帮助学员全面掌握大语言模型的核心技术。并且&#xff0c;课程…...

Docker+Flask 实战:打造高并发微服务架构

DockerFlask 实战&#xff1a;打造高并发微服务架构 今天我们要深入探讨一个非常热门且实用的主题&#xff1a;基于 Docker 部署 Python Flask 应用。Docker 作为当下最流行的容器化技术&#xff0c;已经广泛应用于各种开发和部署场景&#xff0c;尤其是在微服务架构中。而 Fl…...

前端跨域如何调试,以及相关概念梳理【环境变量 本地代理 正向代理 反向代理 OPTIONS请求 CDN 等】

跨域报错 一 前端日常开发时&#xff0c;项目的部署地址和接口请求的地址一般是同源的&#xff0c;不会跨域。 例如项目的测试环境部署在https://my-dev.BeatingWorldLine.com/xxx, 测试环境的访问接口域名也要相同来保证不跨域https://my-dev.BeatingWorldLine.com/api/xxx, …...

【区块链】以太坊

学习视频源链接&#xff1a; https://www.bilibili.com/video/BV1Vt411X7JF/ 本文是根据肖老师的视频进行的笔记记录 bitcoin 1.0 区块链 以太坊 2.0区块链 以太坊 设置了 memory hard mining puzzle &#xff0c;这造成了asic resistance&#xff0c; 后续 proof of work &a…...

MCU的工作原理:嵌入式系统的控制核心

MCU的工作原理可以概括为以下几个步骤&#xff1a; 1. 初始化 上电后&#xff0c;MCU从Flash存储器中加载程序代码&#xff0c;并初始化外设和寄存器。 2. 任务执行 根据程序逻辑&#xff0c;MCU执行数据处理、外设控制和通信等任务。通过中断系统实时响应外部事件。 3. 低…...

离线服务器ollama新增qwen2:0.5b模型

离线服务器ollama新增qwen2:0.5b模型 Dify集成ollama前面已经介绍过离线服务器CentOS使用的docker安装的ollama&#xff0c;其中在ollama中已经安装了deepseek-r1:1.5b。目前的需求是需要再安装一个qwen2:0.5b的模型&#xff0c;那么如何安装呢&#xff1f; 1.首先在有网的服…...

Ubuntu20.04安装运行DynaSLAM

目录 一、安装Anaconda 二、相关依赖库安装 1、boost安装 2、Eigen 3安装 3、opencv安装 4、Pangolin安装 三、配置Mask_RCNN环境 四、DynaSLAM编译 五、DynaSLAM运行 一、安装Anaconda 打开以下链接&#xff1a; Index of / 下载和自己系统匹配的安装包。这里下…...

Apache Shiro反序列化漏洞深度剖析:从原理到利用

引言 在Web安全的世界里&#xff0c;反序列化漏洞一直是最危险的漏洞类型之一。今天&#xff0c;我们将深入探讨Apache Shiro框架中的两个著名反序列化漏洞.通过通俗易懂的解释和详细的实例&#xff0c;帮助你理解这类漏洞的本质和危害。 Shiro框架与"记住我"功能简…...

Android UI 组件系列(二):Button 进阶用法

引言 在上一篇博客中&#xff0c;我们介绍了 Button 的基本用法和常见属性&#xff0c;掌握了 Button 的基础知识。然而&#xff0c;在实际开发中&#xff0c;Button 远不止于简单的点击功能&#xff0c;它还可以支持不同的变体、丰富的自定义样式&#xff0c;以及更灵活的状态…...

CentOS-7安装Docker(更新时间:2025-03-12)

CentOS-7安装Docker 该文章记录在CentOS 7上安装Docker的过程和步骤&#xff0c;以及在安装过程中遇到的困难和解决方案。 目录 CentOS-7安装Docker一、环境准备二、安装Docker1.验证服务器是否接入互联网2. 检查CentOS内核版本3.使用root权限登录CentOS。确保yum包更新到最新…...

网络空间安全(31)安全巡检

一、定义与目的 定义&#xff1a; 安全巡检是指由专业人员或特定部门负责&#xff0c;对各类设施、设备、环境等进行全面或重点检查&#xff0c;及时发现潜在的安全隐患或问题。 目的&#xff1a; 预防事故发生&#xff1a;通过定期的安全巡检&#xff0c;及时发现并解决潜在的…...

Kubernetes学习笔记-移除Nacos迁移至K8s

项目服务的配置管理和服务注册发现由原先的Nacos全面迁移到Kubernetes上。 一、移除Nacos 移除Nacos组件依赖。 <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <…...

Docker 构建 nginx-redis-alpine 项目详解

Docker 构建 nginx-redis-alpine 项目详解 一、课程概述 嘿&#xff0c;朋友们&#xff01;今天咱们要深入探索一个超级实用的项目 ——nginx-redis-alpine&#xff01;这个项目可不简单&#xff0c;它包含了好多重要的知识点&#xff0c;像文件目录结构、核心文件的作用及配…...

【教学类-43-26】20240312 数独4宫格的所有可能(图片版 576套样式,空1格-空8格,每套65534张*576小图=3千万张小图)

背景需求&#xff1a; 之前做了三宫格所有可能图片 510小图*12套6120图&#xff0c;所以3分钟就生成了 【教学类-43-25】20240311 数独3宫格的所有可能&#xff08;图片版 12套样式&#xff0c;空1格-空8格&#xff0c;每套510张&#xff0c;共6120小图&#xff09;-CSDN博客…...

ChromeOS 134 版本更新

ChromeOS 134 版本更新 一、ChromeOS 134 更新内容 1. ChromeOS 自助终端&#xff08;Kiosk&#xff09;模式支持隔离 Web 应用&#xff08;Isolated Web Apps&#xff09; 从 ChromeOS 134 开始&#xff0c;自助终端&#xff08;Kiosk&#xff09;模式支持 隔离 Web 应用&a…...

Redis面试篇

目录 Redis面试篇 1.什么是Redis&#xff1f;作用是什么&#xff1f; 2.什么是缓存穿透、缓存击穿、缓存雪崩 2.1缓存穿透 2.2缓存击穿 2.3缓存雪崩 3.redis如何持久化 1. RDB&#xff08;快照存储&#xff09; 2. AOF&#xff08;追加日志&#xff09; 4.Redis 的过…...

C#中通过Response.Headers设置自定义参数

一、基础设置方法 1. 直接添加自定义头 // ASP.NET Core方案 Response.Headers.Append("X-API-Version", "2.3.1"); Response.Headers.Append("Custom-Auth-Token", Guid.NewGuid().ToString());• 底层原理&#xff1a;通过IHeaderDictionary…...

C++标准模板库学习--函数模板返回值参数类型

template<typename T1, typename T2> 2 T1 max (T1 a, T2 b) 3 { 4 return b < a ? a : b; 5 } 6 ... 7 auto m ::max(4, 7.2); // OK, 不过返回类型与第一个参数类型一样 如何解决模板的返回类型 法一&#xff0c;使用decltype进行类型推断&#xff0c;在编译时…...

BUG修复 | 一次钉钉工作台应用远程调试实战(开发者工具)

#1 ℹ️背景故事 最近用户反馈&#xff0c;钉钉工作台的应用无法正常使用&#xff0c;卡在自动登录页面。 天&#xff0c;这是运行10年的老程序&#x1f631;&#xff0c;我当时真是吓得不轻。这老古董完全不记得怎么改了&#x1f602;。 #2 &#x1f41e;开启远程调试 钉钉…...

[目标检测] 训练之前要做什么

背景&#xff1a;训练一个Yolo8模型&#xff0c;在训练之前&#xff0c;数据集的处理是影响效果的关键因素。 Step1 定义规则 什么是人/车&#xff0c;比如人的话可能是站着的人&#xff0c;如果是骑电动车/自行车就不算是人。 Step2 收集数据集 1. 自己标注。如果是自己标…...

一窥DeepSeek开源EPLB项目:揭开技术背后的面纱

摘要 在DeepSeek开源DualPipe项目的同一天&#xff0c;EPLB项目也正式对外公开。EPLB&#xff08;Enhanced Pipeline Balancing&#xff09;并非一蹴而就的奇迹&#xff0c;而是经过长时间的研发与优化。该项目旨在通过改进管道平衡机制&#xff0c;提升系统的稳定性和效率。本…...

达梦数据库中插入导出图片的方法与应用

达梦数据库中插入导出图片的方法与应用 在数据库的实际应用场景中&#xff0c;图片存储是一项常见且重要的需求。以电商平台为例&#xff0c;商品展示图片是吸引消费者的关键元素&#xff1b;而在社交软件里&#xff0c;用户头像更是个人形象的直观体现。针对达梦数据库&#…...

问deepseek: OpenFOAM并行分区后,是如何实现ldumatrix矩阵向量乘法计算逻辑的?

在OpenFOAM中&#xff0c;lduMatrix 是用于存储稀疏矩阵的类&#xff0c;支持并行计算。并行分区后&#xff0c;lduMatrix 的矩阵向量乘法通过以下步骤实现&#xff1a; 1. 矩阵分区 分区&#xff1a;将矩阵和向量分配到多个处理器上&#xff0c;每个处理器负责一部分。接口&…...

linux(ubuntu)中Conda、CUDA安装Xinference报错ERROR: Failed to build (llama-cpp-python)

文章目录 一、常规办法二、继续三、继续四、缺少 libgomp库&#xff08;最终解决&#xff09;在 Conda 环境中安装 libgomp 如果符合标题情况 执行的&#xff1a; pip install "xinference[all]"大概率是最终解决的情况。 一、常规办法 llama-cpp-python 依赖 CMak…...

蓝耘携手通义万象 2.1 图生视频:开启创意无限的共享新时代

在科技飞速发展的今天&#xff0c;各种新奇的技术不断涌现&#xff0c;改变着我们的生活和工作方式。蓝耘和通义万象 2.1 图生视频就是其中两项非常厉害的技术。蓝耘就像是一个超级大管家&#xff0c;能把各种资源管理得井井有条&#xff1b;而通义万象 2.1 图生视频则像是一个…...

04 1个路由器配置一个子网的dhcp服务

前言 这是最近一个朋友的 ensp 相关的问题, 这里来大致了解一下 ensp, 计算机网络拓扑 相关基础知识 这里一系列文章, 主要是参照了这位博主的 ensp 专栏 这里 我只是做了一个记录, 自己实际操作了一遍, 增强了一些 自己的理解 当然 这里仅仅是一个 简单的示例, 实际场景…...

Android studio运行报错处理

没装HAXM报错&#xff1a; Intel HAXM 7.6.5 下载 下载链接&#xff1a; https://www.filehorse.com/download-intel-haxm/54766/download/#google_vignette 运行时弹窗提示&#xff1a;Device manager The emulator process for AVD Pixel_3a_API_34_extension_level_7_x86_6…...

【CXX】6.7 SharedPtr<T> — std::shared_ptr<T>

std::shared_ptr 的 Rust 绑定称为 SharedPtr。 限制&#xff1a; SharedPtr 不支持 T 为不透明的 Rust 类型。对于在语言边界上传递不透明 Rust 类型的所有权&#xff0c;应改用 Box&#xff08;C 中的 rust::Box&#xff09;。 示例 // src/main.rsuse std::ops::Deref; …...

NocoBase 本周更新汇总:双因素身份认证(2FA)

原文链接&#xff1a;https://www.nocobase.com/cn/blog/weekly-updates-202503013 汇总一周产品更新日志&#xff0c;最新发布可以前往我们的博客查看。 本周我们发布了 NocoBase 1.6.0 版本&#xff0c;带来集群模式部署、安全策略优化和迁移管理等多项新特性。 NocoBase …...

【Go学习】04-1-Gin框架-路由请求响应参数

【Go学习】04-1-Gin框架 初识框架go流行的web框架GinirisBeegofiber Gin介绍Gin快速入门 路由RESTful API规范请求方法URI静态url路径参数模糊匹配 处理函数分组路由 请求参数GET请求参数普通参数数组参数map参数 POST请求参数表单参数JSON参数 路径参数文件参数 响应字符串方式…...

DataX的python3使用

datax这东西本身是python2写的&#xff0c;这导致python3&#xff0c;就各种语法报错&#xff0c;问题是&#xff0c;现在的工程都是python3搞的&#xff0c;这就很难受.... 网上找到一篇帖子&#xff0c;可以解决这个问题&#xff1a; 原帖&#xff1a;python3执行datax报错…...

部署项目至服务器:响应时间太长,无法访问此页面?

在我们部署项目到服务器上的时候&#xff0c;一顿操作猛如虎&#xff0c;打开页面..... 这里记录一下这种情况是怎么回事。一般就是服务器上的安全组没有放行端口。 因为我是用宝塔进行项目部署的。所以遇到这种情况&#xff0c;要去操作两边&#xff08;宝塔and服务器所属平台…...

Map<String,Object>中Fastjson提取entrys对应的值

今天在处理接口数据时&#xff0c;需要解析出对方传入的json数据&#xff0c;并需要取出其中一个字段的值来判断&#xff0c;记录下我的步骤&#xff0c;提供参考&#xff1a; 1.json数据准备 {"hrOrgUnit": "00000000-0000-0000-0000-000000000000CCE7AED4&q…...

【毕业论文格式】word分页符后的标题段前间距消失

文章目录 【问题描述】 分页符之后的段落开头&#xff0c;明明设置了标题有段前段后间距&#xff0c;但是没有显示间距&#xff1a; 【解决办法】 选中标题&#xff0c;选择边框 3. 选择段前间距&#xff0c;1~31磅的一个数 结果...

Android,Java,Kotlin 确保线程顺序执行的多种实现方式

在多线程编程中&#xff0c;有时需要确保一个线程必须等待另一个线程执行完毕后再执行。本文将介绍几种常见的方法来实现这一需求&#xff0c;并提供详细的代码示例。 1. 使用 Thread.join() Thread.join() 是最简单直接的方法&#xff0c;它会让当前线程等待目标线程执行完毕…...

AWK 入门教程:强大的文本处理工具

AWK 是一种强大的文本处理工具&#xff0c;广泛用于 Linux/Unix 系统中对文本文件或数据流进行操作。它能够基于条件筛选、统计字段、重新排列数据等。主要特点包括&#xff1a; 2. AWK 的基本语法 2.1 AWK 程序的结构 AWK 程序的结构: awk pattern { action } file 2.2 常…...