Linux系统编程-线程同步详解

线程同步是指多个线程协调工作,以便在共享资源的访问和操作过程中保持数据一致性和正确性。在多线程环境中,线程是并发执行的,因此如果多个线程同时访问和修改共享资源,可能会导致数据不一致、竞态条件(race condition)等问题。线程同步通过协调线程的执行顺序和共享资源的访问来避免这些问题。

在多线程编程中,需要线程同步的主要原因包括:

  • 共享资源的安全访问:多个线程可能同时访问和修改共享的数据或资源,如果没有同步机制,可能会导致数据不一致或损坏。

  • 避免竞态条件:竞态条件是指多个线程以不受控制的顺序访问共享资源,从而导致程序执行结果不确定的情况。通过同步机制可以避免竞态条件的发生。

  • 保证程序正确性:线程同步确保多线程程序的行为是可预测和正确的,避免因并发访问而引入的错误。

1.互斥量(Mutex)

互斥量(互斥锁)是一种最基本的同步机制,用于保护临界区(Critical Section),确保在同一时间只有一个线程可以访问共享资源。

它提供了两个主要操作:

  • 加锁(Locking):线程通过加锁操作获取互斥量,如果互斥量已经被其他线程锁定,则当前线程会阻塞,直到获取到锁。
  • 解锁(Unlocking):线程使用解锁操作释放互斥量,允许其他线程获取锁。

通过这种机制,互斥量确保了临界区中的代码在同一时间只能被一个线程执行,从而避免了数据竞争和不一致的问题。

1.1初始化和销毁互斥量

1.1.1pthread_mutex_init()

初始化互斥量,并可选地设置其属性。

int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
参数:
pthread_mutex_t *mutex:指向互斥量变量的指针,用于初始化该互斥量。
const pthread_mutexattr_t *attr:可选参数,指向 pthread_mutexattr_t 类型的指针,用于设置互斥量的属性。如果为 NULL,使用默认属性。
返回值:
成功:返回 0。
失败:返回错误号(例如 EINVAL 表示参数无效)。
  • 初始化互斥量后需要调用 pthread_mutex_destroy 函数来释放其占用的资源。
  • 通常在使用互斥量前调用,确保互斥量的准备和设置。

1.1.2pthread_mutex_destroy()

销毁互斥量,释放其占用的资源。

int pthread_mutex_destroy(pthread_mutex_t *mutex);
参数:
pthread_mutex_t *mutex:指向要销毁的互斥量变量的指针。
返回值:
成功:返回 0。
失败:返回错误号。
  • 在互斥量不再需要时调用,以释放其占用的资源。
  • 仅当确保没有线程在使用该互斥量时才能安全地调用该函数。

1.2加锁和解锁操作

1.2.1pthread_mutex_lock()

加锁操作,阻塞当前线程直到获取锁。

int pthread_mutex_lock(pthread_mutex_t *mutex);
参数:
pthread_mutex_t *mutex:指向要加锁的互斥量变量的指针。
返回值:
成功:返回 0。
失败:返回错误号。
  • 当互斥量已经被其他线程锁定时,当前线程会阻塞直到获取锁。
  • 必须成对使用 pthread_mutex_lockpthread_mutex_unlock 来保护临界区。

1.2.2pthread_mutex_trylock()

尝试加锁操作,非阻塞式,立即返回结果。

int pthread_mutex_trylock(pthread_mutex_t *mutex);
参数:
pthread_mutex_t *mutex:指向要加锁的互斥量变量的指针。
返回值:
成功:返回 0。
失败:返回 EBUSY(互斥量已被锁定)或其他错误号。
  • 如果互斥量已被锁定,则立即返回错误。
  • 适用于需要非阻塞尝试加锁的情况,可以用于避免线程阻塞。

1.2.3pthread_mutex_unlock()

解锁操作,释放互斥量。

int pthread_mutex_unlock(pthread_mutex_t *mutex);
参数:
pthread_mutex_t *mutex:指向要解锁的互斥量变量的指针。
返回值:
成功:返回 0。
失败:返回错误号。
  • 解锁后允许其他线程获取互斥量。
  • 必须在每次成功调用 pthread_mutex_lock 后调用 pthread_mutex_unlock 来释放锁。

示例代码:

创建两个线程来共享一个全局变量 int number,然后每个线程分别对其进行5000次递增操作,同时使用互斥量来保证线程同步。(如果没有锁大家可以看看会发生什么情况)

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>#define NUM_THREADS 2
#define NUM_INCREMENTS 5000int number = 0;
pthread_mutex_t mutex;  // 互斥量变量void* thread_function(void* arg) {int thread_id = *(int*)arg;for (int i = 0; i < NUM_INCREMENTS; ++i) {// 加锁pthread_mutex_lock(&mutex);// 临界区:对共享变量 number 执行操作number++;// 解锁pthread_mutex_unlock(&mutex);}pthread_exit(NULL);
}int main() {pthread_t threads[NUM_THREADS];int thread_ids[NUM_THREADS];// 初始化互斥量if (pthread_mutex_init(&mutex, NULL) != 0) {fprintf(stderr, "Mutex initialization failed\n");return 1;}// 创建两个线程for (int i = 0; i < NUM_THREADS; ++i) {thread_ids[i] = i;if (pthread_create(&threads[i], NULL, thread_function, &thread_ids[i]) != 0) {fprintf(stderr, "Thread creation failed\n");return 1;}}// 等待所有线程结束for (int i = 0; i < NUM_THREADS; ++i) {pthread_join(threads[i], NULL);}// 销毁互斥量pthread_mutex_destroy(&mutex);// 输出最终的 number 值printf("Final value of number: %d\n", number);return 0;
}

1.3死锁(Deadlock)

死锁并不是linux提供给用户的一种使用方法,而是由于用户使用互斥锁不当引起的一种现象。死锁发生在多个并发执行的线程(或进程)之间,主要由于彼此竞争资源而造成。典型的死锁情况涉及两个或多个线程或进程,每个都在等待对方释放其持有的资源,导致所有线程都被阻塞,无法继续执行下去。

常见的死锁有两种:

第一种:自己锁自己,如下图代码片段

第二种 线程A拥有A锁,请求获得B锁;线程B拥有B锁,请求获得A锁,这样造成线程A和线程B都不释放自己的锁,而且还想得到对方的锁,从而产生死锁,如下图所示:

  • 如何解决死锁:
  • 让线程按照一定的顺序去访问共享资源
  • 在访问其他锁的时候,需要先将自己的锁解开
  • 调用pthread_mutex_trylock,如果加锁不成功会立刻返回

2. 条件变量(Condition Variable)

条件变量(Condition Variable) 是一种线程间同步的机制,通常与互斥锁结合使用,用于在某个条件满足时通知其他线程。条件变量允许线程在等待某个特定条件的同时阻塞,直到另一个线程显式地通知条件已经满足或者超时。条件本身不是锁!但它也可以造成线程阻塞。通常与互斥锁配合使用。给多线程提供一个会合的场所。

  • 使用互斥量保护共享数据。
  • 使用条件变量可以使线程阻塞, 等待某个条件的发生, 当条件满足的时候解除阻塞。

主要作用包括:

  • 等待条件:使线程能够在满足特定条件之前进入休眠状态,节省系统资源。
  • 条件满足时通知:一旦其他线程改变了条件,可以通过条件变量通知正在等待的线程继续执行。

2.1相关操作函数

2.1.1pthread_cond_init()

初始化条件变量 cond

int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
参数:
pthread_cond_t *restrict cond:指向要初始化的条件变量的指针。
const pthread_condattr_t *restrict attr:可选参数,指向 pthread_condattr_t 类型的指针,用于设置条件变量的属性。通常可以设置为 NULL,表示使用默认属性。
返回值:
成功:返回 0。
失败:返回错误号(例如 EINVAL 表示参数无效)。
  • 初始化条件变量后,应当使用 pthread_cond_destroy 函数来释放其占用的资源。
  • 通常在创建条件变量后立即调用该函数进行初始化。

2.1.2pthread_cond_destroy()

销毁条件变量 cond,释放其占用的资源。

int pthread_cond_destroy(pthread_cond_t *cond);
参数:
pthread_cond_t *cond:指向要销毁的条件变量的指针。
返回值:
成功:返回 0。
失败:返回错误号。
  • 在条件变量不再需要时调用,以释放其占用的资源。
  • 确保没有线程在使用该条件变量时才能安全地调用该函数。

2.1.3pthread_cond_wait()

阻塞当前线程,等待条件变量 cond 被其他线程信号唤醒。

int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
参数:
pthread_cond_t *restrict cond:指向要等待的条件变量的指针。
pthread_mutex_t *restrict mutex:与条件变量相关联的互斥锁,用于避免竞态条件。
返回值:
成功:返回 0。
失败:返回错误号。
  • 调用该函数前必须先获取 mutex 锁,函数内部会自动释放 mutex 锁,并在等待期间阻塞当前线程。
  • 当被唤醒时,函数内部会再次获取 mutex 锁,并从函数返回。

2.1.4pthread_cond_timedwait()

在指定的超时时间内阻塞当前线程,等待条件变量 cond 被其他线程信号唤醒。

int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
参数:
pthread_cond_t *restrict cond:指向要等待的条件变量的指针。
pthread_mutex_t *restrict mutex:与条件变量相关联的互斥锁。
const struct timespec *restrict abstime:指定的超时时间,为绝对时间。
返回值:
成功:返回 0。
失败:返回错误号。
  • pthread_cond_wait 类似,但可以设置超时时间,在超时之后会自动返回并解除阻塞。
  • 如果超时时间设置为 NULL,则函数将无限期地等待,直到条件变量被信号唤醒。

2.1.5pthread_cond_signal()

唤醒等待在条件变量 cond 上的一个线程。

int pthread_cond_signal(pthread_cond_t *cond);
参数:
pthread_cond_t *cond:指向要唤醒的条件变量的指针。
返回值:
成功:返回 0。
失败:返回错误号。
  • 唤醒等待在条件变量上的一个线程,如果有多个线程等待,则可能唤醒任意一个。
  • 唤醒后,被唤醒的线程将尝试重新获取与条件变量关联的互斥锁。

示例代码:

3. 读写锁(Read-Write Lock)

读写锁允许多个线程同时对共享资源进行读取操作,但是写操作时需要排他性,即同一时刻只能有一个线程进行写操作。这种区分读和写的方式能够有效地提高系统的并发性能,特别适用于数据结构中读操作远远多于写操作的情况。当读写锁以读模式锁住时,它是以共享模式锁住的;当它以写模式锁住时,它是以独占模式锁住的。写独占、读共享。

读写锁特性:

  • 读写锁是“写模式加锁”时,解锁前,所有对该锁加锁的线程都会被阻塞。
  • 读写锁是“读模式加锁”时,如果线程以读模式对其加锁会成功;如果线程以写模式加锁会阻塞。
  • 读写锁是“读模式加锁”时, 既有试图以写模式加锁的线程,也有试图以读模式加锁的线程。那么读写锁会阻塞随后的读模式锁请求。优先满足写模式锁。读锁、写锁并行阻塞,写锁优先级高

3.1初始化和销毁读写锁

3.1.1pthread_rwlock_init()

初始化读写锁 rwlock,可以选择性地设置其属性。

int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);
参数:
pthread_rwlock_t *restrict rwlock:指向要初始化的读写锁变量的指针。
const pthread_rwlockattr_t *restrict attr:可选参数,指向 pthread_rwlockattr_t 类型的指针,用于设置读写锁的属性。通常可以设置为 NULL,表示使用默认属性。
返回值:
成功:返回 0。
失败:返回错误号(例如 EINVAL 表示参数无效)。
  • 初始化读写锁后,应当使用 pthread_rwlock_destroy 函数来释放其占用的资源。
  • 通常在创建读写锁后立即调用该函数进行初始化。

3.1.2pthread_rwlock_destroy()

销毁读写锁 rwlock,释放其占用的资源。

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
参数:
pthread_rwlock_t *rwlock:指向要销毁的读写锁变量的指针。
返回值:
成功:返回 0。
失败:返回错误号。
  • 在读写锁不再需要时调用,以释放其占用的资源。
  • 确保没有线程在使用该读写锁时才能安全地调用该函数。

3.2读写锁加锁和解锁操作:

3.2.1pthread_rwlock_rdlock()

加读锁,允许多个线程同时对共享资源进行读取操作。

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
参数:
pthread_rwlock_t *rwlock:指向要加读锁的读写锁变量的指针。
返回值:
成功:返回 0。
失败:返回错误号。
  • 如果有其他线程持有写锁或者有线程在请求写锁,则当前线程将被阻塞,直到获取读锁为止。
  • 多个线程可以同时获取读锁,不会相互阻塞。

3.2.2pthread_rwlock_wrlock()

加写锁,确保只有一个线程可以对共享资源进行写操作,期间禁止其他线程的读或写操作。

int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
参数:
pthread_rwlock_t *rwlock:指向要加写锁的读写锁变量的指针。
返回值:
成功:返回 0。
失败:返回错误号。
  • 如果有其他线程持有读锁或写锁,则当前线程将被阻塞,直到获取写锁为止。
  • 只能有一个线程可以同时持有写锁,其他线程无法同时获取读锁或写锁。

3.2.3pthread_rwlock_tryrdlock()

尝试加读锁,非阻塞方式。如果不能立即获得锁,则立即返回。

int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
参数:
pthread_rwlock_t *rwlock:指向要加读锁的读写锁变量的指针。
返回值:
成功:返回 0。
失败:返回 EBUSY(读写锁已被写锁占用)或其他错误号。
  • 如果不能立即获取读锁,则该函数立即返回失败。
  • 适用于需要检测是否可以立即读取共享资源的场景。

3.2.4pthread_rwlock_trywrlock()

尝试加写锁,非阻塞方式。如果不能立即获得锁,则立即返回。

int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
参数:
pthread_rwlock_t *rwlock:指向要加写锁的读写锁变量的指针。
返回值:
成功:返回 0。
失败:返回 EBUSY(读写锁已被其他线程占用)或其他错误号。
  • 如果不能立即获取写锁,则该函数立即返回失败。
  • 适用于需要检测是否可以立即写入共享资源的场景。

3.2.5pthread_rwlock_unlock()

解锁操作,释放之前加的读锁或写锁。

int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
参数:
pthread_rwlock_t *rwlock:指向要解锁的读写锁变量的指针。
返回值:
成功:返回 0。
失败:返回错误号。
  • 解锁后允许其他线程获取读锁或写锁。
  • 必须在每次成功调用 pthread_rwlock_rdlockpthread_rwlock_wrlock 后调用该函数来释放锁。

示例代码:

其中包括3个写线程和5个读线程对同一个全局资源进行操作。每个线程都会不定时地访问和修改这个全局资源,并通过读写锁确保线程之间的同步。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h> // 用于随机睡眠时间#define NUM_READERS 5
#define NUM_WRITERS 3pthread_rwlock_t rwlock;
int global_resource = 0;void* writer(void* arg) {int thread_id = *(int*)arg;while (1) {// 模拟写操作sleep(rand() % 3); // 随机睡眠时间// 加写锁pthread_rwlock_wrlock(&rwlock);// 写操作global_resource++;printf("Writer %d writes: global_resource = %d\n", thread_id, global_resource);// 解锁pthread_rwlock_unlock(&rwlock);}return NULL;
}void* reader(void* arg) {int thread_id = *(int*)arg;while (1) {// 模拟读操作sleep(rand() % 2); // 随机睡眠时间// 加读锁pthread_rwlock_rdlock(&rwlock);// 读操作printf("Reader %d reads: global_resource = %d\n", thread_id, global_resource);// 解锁pthread_rwlock_unlock(&rwlock);}return NULL;
}int main() {pthread_t writers[NUM_WRITERS], readers[NUM_READERS];int writer_ids[NUM_WRITERS], reader_ids[NUM_READERS];int i;// 初始化读写锁pthread_rwlock_init(&rwlock, NULL);// 创建写线程for (i = 0; i < NUM_WRITERS; ++i) {writer_ids[i] = i + 1;pthread_create(&writers[i], NULL, writer, &writer_ids[i]);}// 创建读线程for (i = 0; i < NUM_READERS; ++i) {reader_ids[i] = i + 1;pthread_create(&readers[i], NULL, reader, &reader_ids[i]);}// 等待所有写线程结束for (i = 0; i < NUM_WRITERS; ++i) {pthread_join(writers[i], NULL);}// 等待所有读线程结束for (i = 0; i < NUM_READERS; ++i) {pthread_join(readers[i], NULL);}// 销毁读写锁pthread_rwlock_destroy(&rwlock);return 0;
}

4. 信号量(Semaphore)

信号量是由一个整型变量和相关的操作集合组成,用于控制对共享资源的访问。信号量可以看作是一个计数器,用于表示可用的资源数量,线程或进程在访问资源前必须首先获取信号量,访问结束后释放信号量。

主要作用包括:

  • 同步:控制多个线程或进程的执行顺序,保证在某些条件下的有序执行。
  • 互斥:保证对共享资源的访问是排他的,避免多个线程或进程同时修改资源造成的数据不一致性问题。

4.2相关操作函数

4.2.1sem_init()

初始化一个信号量 sem

int sem_init(sem_t *sem, int pshared, unsigned int value);
参数:
sem_t *sem:指向要初始化的信号量的指针。
int pshared:指定信号量是进程共享(非零)还是线程共享(零)。
unsigned int value:信号量的初始值,表示可用资源的数量。
返回值:
成功:返回 0。
失败:返回 -1,并设置 errno 来指示错误原因。
  • 信号量初始化后需要通过 sem_destroy 函数进行清理。
  • 如果 pshared 是非零,则信号量可以在多个进程间共享,通常使用在进程间通信(IPC)的场景中。

4.2.2sem_destroy()

销毁一个已经初始化的信号量 sem。释放由信号量占用的资源,确保在信号量不再需要时调用。

int sem_destroy(sem_t *sem);
参数:
sem_t *sem:指向要销毁的信号量的指针。
返回值:
成功:返回 0。
失败:返回 -1,并设置 errno 来指示错误原因。

4.2.3.sem_wait()

对信号量 sem 进行 P 操作(等待操作)。

int sem_wait(sem_t *sem);
参数:
sem_t *sem:指向要操作的信号量的指针。
返回值:
成功:返回 0。
失败:返回 -1,并设置 errno 来指示错误原因。
  • 如果信号量的值大于 0,将其递减;否则阻塞当前线程,直到信号量的值大于 0
  • 该函数执行时需要保证线程安全,通常与互斥锁结合使用以避免竞态条件。

4.2.4sem_trywait()

尝试对信号量 sem 进行 P 操作的非阻塞版本。与 sem_wait 不同的是,如果信号量的值为 0,立即返回而不阻塞线程。

int sem_trywait(sem_t *sem);
参数:
sem_t *sem:指向要操作的信号量的指针。
返回值:
成功:返回 0。
如果信号量的值为 0,表示资源不可用,立即返回 -1(不阻塞),并设置 errno 为 EAGAIN。
其他失败情况返回 -1,并设置 errno 来指示错误原因。

4.2.5sem_post()

对信号量 sem 进行 V 操作(释放操作)。

int sem_post(sem_t *sem);
参数:
sem_t *sem:指向要操作的信号量的指针。
返回值:
成功:返回 0。
失败:返回 -1,并设置 errno 来指示错误原因。
  • 将信号量的值递增,如果有线程因等待该信号量而被阻塞,将会唤醒其中一个线程。
  • 释放操作通常在资源使用完毕后调用,通知其他线程可以继续访问该资源。

4.2.6sem_getvalue()

获取信号量 sem 的当前值。sem_getvalue 函数可以获取信号量的当前值,而无需对其进行修改。

int sem_getvalue(sem_t *restrict sem, int *restrict sval);
参数:
sem_t *restrict sem:指向要获取值的信号量的指针。
int *restrict sval:用于存储信号量当前值的整型指针。
返回值:
成功:返回 0,并将当前信号量的值存储在 sval 中。
失败:返回 -1,并设置 errno 来指示错误原因。

示例代码:

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

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

相关文章

面试题008-Java-SpringBoot

面试题008-Java-SpringBoot 目录 面试题008-Java-SpringBoot题目自测题目答案1. Spring 和 Spring Boot有什么区别&#xff1f;2. Spring Boot 的主要优点是什么&#xff1f;3. 什么是Spring Boot Starter&#xff1f;4. 介绍一下SpringBootApplication注解&#xff1f;5. Spri…

【密码学】消息认证

你发送给朋友一条消息&#xff08;内容&#xff1a;明天下午来我家吃饭&#xff09;&#xff0c;这一过程中你不想让除你朋友以外的人看到消息的内容&#xff0c;这就叫做消息的机密性&#xff0c;用来保护消息机密性的方式被叫做加密机制。 现在站在朋友的视角&#xff0c;某一…

使用PyQt5实现添加工具栏、增加SwitchButton控件

前言&#xff1a;通过在网上找到的“电池电压监控界面”&#xff0c;学习PyQt5中添加工具栏、增加SwitchButton控件&#xff0c;在滑块控件右侧增加文本显示、设置界面背景颜色、修改文本控件字体颜色等。 1. 上位机界面效果展示 网络上原图如下&#xff1a; 自己使用PyQt5做…

springboot异常(一):springboot自定义全局异常处理

&#x1f337;1. 自定义一个异常类 自定义一个异常&#xff0c;有两个变量异常代码、异常消息&#xff0c;定义了两个构造方法&#xff0c;一个无参构造方法&#xff0c;一个所有参数构造方法。 在构造方法中要掉用父类的构造方法&#xff0c;主要目的是在日志或控制台打印异…

【Linux】多线程_3

文章目录 九、多线程3. C11中的多线程4. 线程的简单封装 未完待续 九、多线程 3. C11中的多线程 Linux中是根据多线程库来实现多线程的&#xff0c;C11也有自己的多线程&#xff0c;那它的多线程又是怎样的&#xff1f;我们来使用一些C11的多线程。 Makefile&#xff1a; te…

Linux - 探索命令行

探索命令行 Linux命令行中的命令使用格式都是相同的: 命令名称 参数1 参数2 参数3 ...参数之间用任意数量的空白字符分开. 关于命令行, 可以先阅读一些基本常识. 然后我们介绍最常用的一些命令: ls用于列出当前目录(即"文件夹")下的所有文件(或目录). 目录会用蓝色…

面试经典题型:调用HashMap的put方法的具体执行流程

在调用put方法时时&#xff0c;有几个关键点需要考虑&#xff1a; 哈希冲突的发生与解决&#xff1a; 哈希冲突指不同的键通过哈希函数计算得到相同的哈希值&#xff0c;导致它们应该存放在哈希表的同一个位置。解决冲突的常用方法包括开放寻址法和链表法&#xff08;或其升级形…

CSIP-FTE考试专业题

靶场下载链接&#xff1a; https://pan.baidu.com/s/1ce1Kk0hSYlxrUoRTnNsiKA?pwdha1x pte-2003密码&#xff1a;admin123 centos:root admin123 解压密码&#xff1a; PTE考试专用 下载好后直接用vmware打开&#xff0c;有两个靶机&#xff0c;一个是基础题&#x…

【CTF-Crypto】数论基础-02

【CTF-Crypto】数论基础-02 文章目录 【CTF-Crypto】数论基础-021-16 二次剩余1-20 模p下-1的平方根*1-21 Legendre符号*1-22 Jacobi符号*2-1 群*2-2 群的性质2-3 阿贝尔群*2-4 子群2-11 群同态2-18 原根2-21 什么是环2-23 什么是域2-25 子环2-26 理想2-32 多项式环 1-16 二次剩…

打造智慧校园德育管理,提升学生操行基础分

智慧校园的德育管理系统内嵌的操行基础分功能&#xff0c;是对学生日常行为规范和道德素养进行量化评估的一个创新实践。该功能通过将抽象的道德品质转化为具体可量化的指标&#xff0c;如遵守纪律、尊师重道、团结协作、爱护环境及参与集体活动的积极性等&#xff0c;为每个学…

医疗器械FDA |FDA网络安全测试具体内容

医疗器械FDA网络安全测试的具体内容涵盖了多个方面&#xff0c;以确保医疗器械在网络环境中的安全性和合规性。以下是根据权威来源归纳的FDA网络安全测试的具体内容&#xff1a; 一、技术文件审查 网络安全计划&#xff1a;制造商需要提交网络安全计划&#xff0c;详细描述产…

Matlab【光伏预测】基于雪融优化算法SAO优化高斯过程回归GPR实现光伏多输入单输出预测附代码

% 光伏预测 - 基于SAO优化的GPR % 数据准备 % 假设有多个输入特征 X1, X2, …, Xn 和一个目标变量 Y % 假设数据已经存储在 X 和 Y 中&#xff0c;每个变量为矩阵&#xff0c;每行表示一个样本&#xff0c;每列表示一个特征 % 参数设置 numFeatures size(X, 2); % 输入特征的…

Spring Boot集成easyposter快速入门Demo

1.什么是easyposter&#xff1f; easyposter是一个简单的,便于扩展的绘制海报工具包 使用场景 在日常工作过程中&#xff0c;通常一些C端平台会伴随着海报生成与分享业务。因为随着移动互联网的迅猛发展&#xff0c;社交分享已成为我们日常生活的重要组成部分。海报分享作为…

visual studio 2019版下载以及与UE4虚幻引擎配置(过程记录)(官网无法下载visual studio 2019安装包)

一、概述 由于需要使用到UE4虚幻引擎&#xff0c;我使用的版本是4.27版本的&#xff0c;其官方默认的visual studio版本是2019版本的&#xff0c;相应的版本对应关系可以通过下面的官方网站对应关系查询。https://docs.unrealengine.com/4.27/zh-CN/ProductionPipelines/Develo…

MMSegmentation笔记

如何训练自制数据集&#xff1f; 首先需要在 mmsegmentation/mmseg/datasets 目录下创建一个自制数据集的配置文件&#xff0c;以我的苹果叶片病害分割数据集为例&#xff0c;创建了mmsegmentation/mmseg/datasets/appleleafseg.py 可以看到&#xff0c;这个配置文件主要定义…

python:使用matplotlib库绘制图像(四)

作者是跟着http://t.csdnimg.cn/4fVW0学习的&#xff0c;matplotlib系列文章是http://t.csdnimg.cn/4fVW0的自己学习过程中整理的详细说明版本&#xff0c;对小白更友好哦&#xff01; 四、条形图 1. 一个数据样本的条形图 条形图&#xff1a;常用于比较不同类别的数量或值&…

3dmax-vray5大常用材质设置方法

3dmax云渲染平台——渲染100 以高性价比著称&#xff0c;是预算有限的小伙伴首选。 15分钟0.2,60分钟内0.8;注册填邀请码【7788】可领30元礼包和免费渲染券 提供了多种机器配置选择(可以自行匹配环境)最高256G大内存机器&#xff0c;满足不同用户需求。 木纹材质 肌理调整&…

函数语意学(The Sematics of Function)

1、非静态成员函数转化为非成员函数 c 设计准则之一就是&#xff1a;非静态成员函数至少和非成员函数有相同的效率。 也就是说下面两个函数具有相同的效率&#xff1a; float magnitude(const Point3d * this){...}; float Point3d::magnitude(){...};以 float Point3d::mag…

练习9.5 彩票分析

练习 9.14&#xff1a;彩票 创建⼀个列表或元素&#xff0c;其中包含 10 个数和 5 个字 ⺟。从这个列表或元组中随机选择 4 个数或字⺟&#xff0c;并打印⼀条消息&#xff0c; 指出只要彩票上是这 4 个数或字⺟&#xff0c;就中⼤奖了。 练习 9.15&#xff1a;彩票分析 可以使…

面试题 05. 替换空格

05. 替换空格 题目描述示例 题解 题目描述 请实现一个函数&#xff0c;把字符串 s 中的每个空格替换成"%20"。 示例 示例1 输入&#xff1a;s “We are happy.” 输出&#xff1a;“We%20are%20happy.” 题解 class Solution { public:string replaceSpace(stri…