线程同步66666

 1. 概述

  • 当有多个线程访问同一个共享资源(临界资源)时,且不允许同时访问,那么就需要线程同步。
  • 常见的线程同步方式:互斥锁、读写锁、条件变量、信号量。

2. 互斥锁

互斥锁的方式可以简单概括为:锁定操作临界资源的代码片段,锁定后每次只能由一个线程来进行操作。这样能够解决多个线程同时访问临界资源造成的数据混乱,但是降低了执行效率(因为并行操作变成了串行操作)。

【1】互斥锁类型:pthread_mutex_t。创建一个该类型的变量就得到一把互斥锁。该变量中保存了锁的状态(打开还是锁定),若为锁定则保存了加锁的线程ID。锁定时其他线程将会阻塞,直到这个互斥锁被打开。

以下函数的返回值:成功返回0,失败返回错误号。

【2】初始化互斥锁:

//restrict:是一个修饰指针的关键字,该关键字修饰的指针可以访问指向的内存地址,其他指针不行
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
//mutex: 互斥锁地址
//attr: 互斥锁属性,一般为NULL默认属性

【3】释放互斥锁资源:

int pthread_mutex_destroy(pthread_mutex_t *mutex);

【4】加锁(阻塞):

int pthread_mutex_lock(pthread_mutex_t *mutex);
//若锁是打开的,那么加锁成功,锁会记录线程ID。
//若锁是锁定的,那么加锁失败,线程阻塞在此,直到上一个线程解锁。

【5】加锁(非阻塞):

int pthread_mutex_trylock(pthread_mutex_t *mutex);
//若加锁失败,则不会阻塞,而是直接返回错误号。

【6】解锁:

int pthread_mutex_unlock(pthread_mutex_t *mutex);
//解锁还需加锁人,哪个线程加的锁就得哪个线程来解锁。

程序实例:创建两个子线程对全局变量number进行+1操作。若不使用互斥锁,就会造成数据混乱。使用了互斥锁,运行正常。

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>pthread_mutex_t mutex;  //全局的互斥锁
int number = 0;         //全局变量//线程t1执行函数
void* funA(void *arg)
{for (int i = 0; i < 50; i++) {pthread_mutex_lock(&mutex);int cur = number;cur++;usleep(10);number = cur;printf("线程1的ID: %ld, number: %d\n", pthread_self(), number);pthread_mutex_unlock(&mutex);}return NULL;
}//线程t2执行函数
void* funB(void *arg)
{for (int i = 0; i < 50; i++) {pthread_mutex_lock(&mutex);int cur = number;cur++;number = cur;printf("线程2的ID: %ld, number: %d\n", pthread_self(), number);usleep(5);pthread_mutex_unlock(&mutex);}return NULL;
}//主函数
int main(int argc, char **argv)
{/* 初始化互斥锁 */pthread_mutex_init(&mutex, NULL);/* 创建两个子线程 */pthread_t t1, t2;pthread_create(&t1, NULL, funA, NULL);pthread_create(&t2, NULL, funB, NULL);/* 阻塞回收两个子线程*/pthread_join(t1, NULL);pthread_join(t2, NULL);/* 销毁互斥锁 */pthread_mutex_destroy(&mutex);return 0;
}

3. 死锁

  • 互斥锁若使用不当,就会造成死锁。一旦多线程造成死锁,就会使得所有线程处于阻塞状态,且无法解除。
  • 常见的死锁场景:①加锁后忘记解锁;②重复加锁;③存在多个共享资源,随意加锁。
  • 避免死锁:①多检查代码;②对共享资源访问完毕后解锁,或者在加锁时使用trylock非阻塞;③引入一些专门用于死锁检测的模块;④如果程序中有多把锁, 可以控制对锁的访问顺序。另外,在加其它锁之前先释放拥有的互斥锁。

4. 自旋锁

【1】介绍:

  • 自旋锁与互斥锁类似,但是自旋锁在试图上锁时是不断“自旋”查看锁是否被释放,CPU资源占用多。因此,使用自旋锁的效率不高,在用户程序中很少使用自旋锁,更多的是在内核中使用针对执行时间少的代码
  • 互斥锁是基于自旋锁实现的,所以自旋锁更加底层。

【2】自旋锁类型:pthread_spinlock_t。创建一个该类型的变量就得到一把自旋锁。

以下函数的返回值:成功返回0,失败返回错误号。

【3】初始化/销毁自旋锁:

int pthread_spin_init(pthread_spinlock_t *lock, int pshared);//初始化
//lock: 自旋锁的地址
//pshared: 表示自旋锁的进程共享属性//PTHREAD_PROCESS_SHARED: 共享自旋锁。该自旋锁可以在多个进程中的线程之间共享//PTHREAD_PROCESS_PRIVATE: 私有自旋锁。只有本进程内的线程才能够使用该自旋锁int pthread_spin_destroy(pthread_spinlock_t *lock);//销毁

【4】自旋锁的加锁和解锁:

int pthread_spin_lock(pthread_spinlock_t *lock);     //加锁(一直自旋)
int pthread_spin_trylock(pthread_spinlock_t *lock);  //加锁(返回错误,错误码EBUSY)
int pthread_spin_unlock(pthread_spinlock_t *lock);   //解锁

程序实例:创建两个子线程来对全局变量+1(每个线程循环10000次)。

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>int count = 0;//全局变量(临界区资源)
pthread_spinlock_t locker;//自旋锁/* 线程执行函数 */
void* doing(void* arg)
{for (int i = 0; i < 10000; ++i) {//自旋锁加锁pthread_spin_lock(&locker);//操作临界区资源int tmp = count;tmp++;count = tmp;//自旋锁解锁pthread_spin_unlock(&locker);}return NULL;
}/* 主函数 */
int main(int argc, char** argv)
{//初始化自旋锁pthread_spin_init(&locker, PTHREAD_PROCESS_PRIVATE);//创建两个子线程pthread_t tid[2];for (int i = 0; i < 2; ++i) {if (pthread_create(&tid[i], NULL, doing, NULL)) {perror("pthread_create error");exit(-1);}}//阻塞回收子线程for (int i = 0; i < 2; ++i) {if (pthread_join(tid[i], NULL)) {perror("pthread_join error");exit(-1);}}//打印全局变量printf("count = %d\n", count);//销毁自旋锁pthread_spin_destroy(&locker);return 0; 
}

5. 读写锁

读写锁可以视为互斥锁的升级版,可以指定锁定的是读操作还是写操作,且同一时间内只能锁定其中一个操作。读写锁的使用方式与互斥锁相同。

【1】读写锁类型:pthread_rwlock_t。创建一个该类型的变量就得到一把读写锁。读写锁中保存了以下信息:①锁的状态(打开还是锁定);②锁的是哪个操作(读/写);③哪个线程锁定了这把锁。

【2】读写锁特点:

  • 使用读写锁的读锁锁定了临界区,那么读操作是并行的。
  • 使用读写锁的写锁锁定了临界区,那么写操作是串行的。
  • 使用读写锁的读锁和写锁分别锁定了两个临界区,那么访问写锁的线程优先进入。因为写锁的优先级高于读锁。

以下函数的返回值:成功返回0,失败返回错误号。

【3】初始化读写锁:

//restrict:是一个修饰指针的关键字,该关键字修饰的指针可以访问指向的内存地址,其他指针不行
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t *restrict attr);
//rwlock: 读写锁地址
//attr: 读写锁属性,一般为NULL默认属性

【3】释放读写锁资源:

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

【4】锁定读操作(阻塞):

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
//若读写锁均打开,那么锁定读操作成功。
//若读写锁中读锁锁定,那么锁定读操作成功。因为读锁是共享的。
//若读写锁中写锁锁定,那么会阻塞。

【5】锁定读操作(非阻塞):

int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
//若读写锁均打开,那么锁定读操作成功。
//若读写锁中读锁锁定,那么锁定读操作成功。因为读锁是共享的。
//若读写锁中写锁锁定,那么会返回错误号,而不会阻塞。

【6】锁定写操作(阻塞):

int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
//若读写锁均打开,锁定写操作成功。
//若读写锁中读锁或写锁锁定了,那么就会阻塞。

【7】锁定写操作(非阻塞):

int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
//若读写锁均打开,锁定写操作成功。
//若读写锁中读锁或写锁锁定了,那么返回错误号,而不会阻塞。

【8】解锁:

int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
//不管锁定的是读锁还是写锁,都能解锁。

程序实例:创建3个子线程进行写操作,创建5个子线程进行读操作。它们都针对一个全局变量。


#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>int number = 0;             //全局变量(临界资源)
pthread_rwlock_t rwlocker;  //全局的读写锁/* 写线程执行函数 */
void* writeNum(void *arg)
{while (1) {pthread_rwlock_wrlock(&rwlocker);int cur = number;cur++;number = cur;printf("写操作完毕!number = %d, tid: %ld\n", number, pthread_self());pthread_rwlock_unlock(&rwlocker);usleep(rand() % 100);//让子线程交替写}return NULL;
}/* 读线程执行函数 */
void* readNum(void *arg)
{while (1) {pthread_rwlock_rdlock(&rwlocker);printf("读操作完毕!number = %d, tid: %ld\n", number, pthread_self());pthread_rwlock_unlock(&rwlocker);usleep(rand() % 100);}return NULL;
}/* 主函数 */
int main(int argc, char **argv)
{//初始化读写锁pthread_rwlock_init(&rwlocker, NULL);//创建8个线程,3个为写线程,5个为读线程pthread_t wtid[3];pthread_t rtid[5];for (int i = 0; i < 3; i++) {pthread_create(&wtid[i], NULL, writeNum, NULL);}for (int i = 0; i < 5; i++) {pthread_create(&rtid[i], NULL, readNum, NULL);}//主线程回收8个线程for (int i = 0; i < 3; i++) {pthread_join(wtid[i], NULL);}for (int i = 0; i < 5; i++) {pthread_join(rtid[i], NULL);}//释放读写锁pthread_rwlock_destroy(&rwlocker);return 0;
}

6. 条件变量

条件变量的作用是进行线程的阻塞,而不是线程同步。当满足某个特定条件时才会阻塞线程。一般用于生产者-消费者模型,且和互斥锁相互配合。

【1】条件变量类型:pthread_cond_t。被条件变量阻塞的线程的信息会被记录到该类型的变量中,以便在解除阻塞时使用。

【2】初始化条件变量:

int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
//cond:条件变量的地址
//attr:条件变量的属性,一般为NULL。

【3】释放条件变量资源:

int pthread_cond_destroy(pthread_cond_t *cond);

【4】阻塞线程:

// 线程阻塞函数, 哪个线程调用这个函数, 哪个线程就会被阻塞
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);//mutex是互斥锁,用于线程同步。
//当阻塞线程时,若线程已经对互斥锁mutex上锁,那么会将这把锁打开,这样做是为了避免死锁
//当线程解除阻塞时,函数内部会帮助这个线程再次将这个mutex锁上,继续向下访问临界区

【5】阻塞线程(时间到解除):

// 将线程阻塞一定的时间长度, 时间到达之后, 线程就解除阻塞了
int pthread_cond_timedwait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);// 以下结构体表示的时间是从1971.1.1到某个时间点的时间, 总长度使用秒/纳秒表示
struct timespec {time_t tv_sec;      /* Seconds */long   tv_nsec;     /* Nanoseconds [0 .. 999999999] */
};//使用示例
time_t mytim = time(NULL);	// 1970.1.1 0:0:0 到当前的总秒数
struct timespec tmsp;
tmsp.tv_nsec = 0;
tmsp.tv_sec = time(NULL) + 100;	// 线程阻塞100s

【6】解除阻塞(至少一个线程):

int pthread_cond_signal(pthread_cond_t *cond);

【7】解除阻塞(全部线程):

int pthread_cond_broadcast(pthread_cond_t *cond);

生产者-消费者模型:

①若干个生产者线程:生产商品放入任务队列中,若任务队列满则阻塞,可以使用一个生产者条件变量来控制是否阻塞。

②若干个消费者线程:消费者从任务队列中拿走商品,若任务队列空则阻塞,可以使用一个消费者条件变量来控制是否阻塞。

③任务队列:可以是数组、链表、stl容器等等。

程序实例:使用条件变量实现生产者-消费者模型,生产者线程有5个,往链表头部添加节点;消费者线程也有5个,删除链表头部节点。

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>/* 单链表定义 */
typedef struct LNode {int number;struct LNode *next;
} LNode;/* 全局变量及锁 */
pthread_mutex_t mutex;  //互斥锁
pthread_cond_t cond;    //条件变量
LNode *head = NULL;     //临界资源,单链表/* 生产者执行函数 */
void* produce(void *arg)
{while (1) {//生产商品,往链表头部添加节点pthread_mutex_lock(&mutex);LNode *tmp = (LNode*)malloc(sizeof(LNode));//创建新节点tmp->number = rand() % 100;tmp->next = head;head = tmp;printf("生产完毕! 新节点number: %d, 线程ID: %ld\n", tmp->number, pthread_self());pthread_mutex_unlock(&mutex);//通知消费者拿走商品pthread_cond_broadcast(&cond);sleep(rand() % 3);//生产慢一点}return NULL;
}/* 消费者执行函数 */
void* consume(void *arg)
{while (1) {pthread_mutex_lock(&mutex);//无商品则等待//这里不能用if,可能出现段错误,如下场景://假设消费者线程1进入后阻塞,然后切换到生产者线程,解除其阻塞//然后切换到消费者线程2拿走了商品,此时链表又是空。//接着,切换回消费者线程1,由于if之前已经判定过了,这里直接进行后续操作,//从而出现段错误。因此通过while循环判断,当阻塞解除后也会再次判断。while (head == NULL) {pthread_cond_wait(&cond, &mutex);}//拿走商品,删除链表头部节点LNode *tmp = head;printf("消费完毕! 节点number: %d, 线程ID: %ld\n", tmp->number, pthread_self());head = head->next;free(tmp);pthread_mutex_unlock(&mutex);sleep(rand() % 3);}return NULL;
}/* 主函数 */
int main(int argc, char **argv)
{//初始化锁和条件变量pthread_mutex_init(&mutex, NULL);pthread_cond_init(&cond, NULL);//创建5个生产者线程和5个消费者线程pthread_t ptid[5];pthread_t ctid[5];for (int i = 0; i < 5; i++) {pthread_create(&ptid[i], NULL, produce, NULL);}for (int i = 0; i < 5; i++) {pthread_create(&ctid[i], NULL, consume, NULL);}//主线程回收10个线程for (int i = 0; i < 5; i++) {pthread_join(ptid[i], NULL);}for (int i = 0; i < 5; i++) {pthread_join(ctid[i], NULL);}//释放互斥锁和条件变量pthread_mutex_destroy(&mutex);pthread_cond_destroy(&cond);return 0;
}

7. 信号量

信号量(信号灯)用在多线程的多任务同步中,一个线程完成了某个任务就通过信号量告诉其它线程,其它线程再进行相关操作。信号量也是用于阻塞线程,要保证线程安全,需要信号量与互斥锁一起使用。

【1】信号量类型:sem_t。需要添加头文件<semaphore.h>.

【2】初始化信号量:

int sem_init(sem_t *sem, int pshared, unsigned int value);
//sem: 信号量地址
//pshared: 0表示线程同步,非0表示进程同步
//value: 初始化当前信号量拥有的资源数>=0, 若资源数为0则阻塞

【3】释放信号量资源:

int sem_destroy(sem_t *sem);
//sem: 信号量地址

【4】消耗资源函数(阻塞):

//sem: 信号量地址
//函数被调用,sem中的资源就会被消耗1个
//当资源数为0时,线程就会阻塞
int sem_wait(sem_t *sem);

【5】消耗资源函数(非阻塞):

//sem: 信号量地址
//函数被调用,sem中的资源就会被消耗1个
//当资源数为0时,线程就会返回错误号,而不会阻塞
int sem_trywait(sem_t *sem);

【6】时间到解除阻塞:

int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
//sem: 信号量地址
//abs_timeout: 与pthread_cond_timedwait中的参数一样

【7】增加资源函数:

//给sem中的资源数+1
//若资源数从0加到1,那么阻塞的线程就会解除阻塞
int sem_post(sem_t *sem);

【8】查看资源数:

//查看信号量sem中的整形数值, 这个值会被写到sval指针对应的内存中
int sem_getvalue(sem_t *sem, int *sval);

程序实例:使用信号量实现生产者-消费者模型,生产者线程有5个,往链表头部添加节点;消费者线程也有5个,删除链表头部节点。同时,限定了平台容纳商品的最大容量为6个。我们可以设置两个信号量,分别来代表生产者线程生产的商品数量、平台中现有的商品数量(用于给消费者线程拿走)。

#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <stdlib.h>
#include <unistd.h>/* 单链表节点声明 */
typedef struct LNode{int number;struct LNode *next;
} LNode;/* 全局变量 */
sem_t psem; //生产者生产商品的信号量
sem_t csem; //消费者拿走时的信号量
pthread_mutex_t mutex;  //互斥锁
LNode *head = NULL;     //单链表(临界区资源)/* 生产者线程执行函数 */
void* produce(void *arg)
{while (1) {//生产者信号量-1sem_wait(&psem);//生产者生产商品pthread_mutex_lock(&mutex);LNode *tmp = (LNode*)malloc(sizeof(LNode));tmp->number = rand() % 100;tmp->next = head;head = tmp;printf("生产完毕!新节点number: %d, 线程ID: %ld\n", tmp->number, pthread_self());pthread_mutex_unlock(&mutex);//消费者信号量+1sem_post(&csem);sleep(rand() % 3);}return NULL;
}/* 消费者线程执行函数 */
void* consume(void *arg)
{while (1) {//消费者信号量-1sem_wait(&csem);//消费者拿走商品pthread_mutex_lock(&mutex);LNode *tmp = head;head = head->next;printf("消费完毕!节点number: %d, 线程ID: %ld\n", tmp->number, pthread_self());free(tmp);pthread_mutex_unlock(&mutex);//生产者信号量+1sem_post(&psem);sleep(rand() % 3);}return NULL;
}/* 主函数 */
int main(int argc, char **argv)
{//初始化信号量和互斥锁sem_init(&psem, 0, 6);//平台最多容纳的商品数sem_init(&csem, 0, 0);//平台最初没有商品pthread_mutex_init(&mutex, NULL);//创建5个生产者线程,5个消费者线程pthread_t ptid[5];pthread_t ctid[5];for (int i = 0; i < 5; i++) {pthread_create(&ptid[i], NULL, produce, NULL);}for (int i = 0; i < 5; i++) {pthread_create(&ctid[i], NULL, consume, NULL);}//释放线程资源for (int i = 0; i < 5; i++) {pthread_join(ptid[i], NULL);}for (int i = 0; i < 5; i++) {pthread_join(ctid[i], NULL);}//释放其它资源sem_destroy(&psem);sem_destroy(&csem);pthread_mutex_destroy(&mutex);return 0;
}

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

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

相关文章

【MYSQL】InnoDB引擎为什么选可重复读作为默认隔离级别

InnoDB引擎为什么选可重复读作为默认隔离级别 一般的DBMS系统&#xff0c;默认都会使用读提交&#xff08;Read-Comitted&#xff0c;RC&#xff09;作为默认隔离级别&#xff0c;如Oracle、SQL Server等&#xff0c;而MySQL却使用可重复读&#xff08;Read-Repeatable&#x…

alphazero学习

AlphaGoZero是AlphaGo算法的升级版本。不需要像训练AlphaGo那样&#xff0c;不需要用人类棋局这些先验知识训练&#xff0c;用MCTS自我博弈产生实时动态产生训练样本。用MCTS来创建训练集&#xff0c;然后训练nnet建模的策略网络和价值网络。就是用MCTSPlayer产生的数据来训练和…

【JVM 的内存模型】

1. JVM内存模型 下图为JVM内存结构模型&#xff1a; 两种执行方式&#xff1a; 解释执行&#xff1a;JVM是由C语言编写的&#xff0c;其中有C解释器&#xff0c;负责先将Java语言解释翻译为C语言。缺点是经过一次JVM翻译&#xff0c;速度慢一点。JIT执行&#xff1a;JIT编译器…

ubuntu设置开启自动挂载sftp

1. 前言 与其说 ubuntu 开启自动挂载 sftp, 更确切的说应该是 nautilus (ubuntu上默认的文件管理器) 开机自动挂载 sftp。 因为 这里即使选择永远记住&#xff0c;开机也不会自动挂载 sftp 2.设置方法 gnome-session-properties #开机只启动设置命令设置 gio mount sftp…

经典双运算放大器LM358

前言 LM358双运放有几十年的历史了吧&#xff1f;通用运放&#xff0c;很常用&#xff0c;搞电路的避免不了接触运放&#xff0c;怎么选择运放&#xff0c;是工程师关心的问题吧&#xff1f; 从本文开始&#xff0c;将陆续发一些常用的运放&#xff0c;大家选型可以参考&#…

浪潮信息携手算力企业为华东产业集群布局提供高质量算力支撑

随着信息技术的飞速发展&#xff0c;算力已成为推动数字经济发展的核心力量。近日&#xff0c;浪潮信息与五家领先的算力运营公司在南京正式签署战略合作协议&#xff0c;共同加速华东地区智算基础设施布局&#xff0c;为区域经济发展注入新动力。 进击的算力 江苏持续加码智算…

springboot三层架构详细讲解

目录 springBoot三层架构0.简介1.各层架构1.1 Controller层1.2 Service层1.3 ServiceImpl1.4 Mapper1.5 Entity1.6 Mapper.xml 2.各层之间的联系2.1 Controller 与 Service2.2 Service 与 ServiceImpl2.3 Service 与 Mapper2.4 Mapper 与 Mapper.xml2.5 Service 与 Entity2.6 C…

Exploting an API endpoiint using documentation

HTTP request methods https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods 第一步:burp抓包刷新页面 httphistory中只能看到两个记录,可以看下Response,是HTML页面,说明这里有HTML页面 ,但是没有发现特定的API接口。 第二步:用户登录 转到用户登录的功能点处…

Nacos源码分析:心跳机制、健康检查、服务发现、AP集群

文章目录 心跳机制与服务健康检查NacosClient端NacosServer端NacosServer端健康检查 服务发现NacosClient端NacosServer端 AP集群从源码启动集群心跳设计原理各节点状态同步服务实例数据同步服务实例状态变动同步 心跳机制与服务健康检查 官方文档&#xff1a;发送某个实例的心…

基于GWO灰狼优化的多目标优化算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 4.1灰狼优化算法原理 4.2 多目标优化问题(MOP)的帕累托最优解 4.3 基于GWO的多目标优化算法 5.完整程序 1.程序功能描述 基于GWO灰狼优化的多目标优化算法matlab仿真&#xff0c;目标函数…

Linux多进程和多线程(六)进程间通信-共享内存

多进程(六) 共享内存共享内存的创建 示例: 共享内存删除 共享内存映射 共享内存映射的创建解除共享内存映射示例:写入和读取共享内存中的数据 写入: ### 读取: 大致操作流程: 多进程(六) 共享内存 共享内存是将分配的物理空间直接映射到进程的⽤户虚拟地址空间中, 减少数据在…

Java | Leetcode Java题解之第217题存在重复元素

题目&#xff1a; 题解&#xff1a; class Solution {public boolean containsDuplicate(int[] nums) {Set<Integer> set new HashSet<Integer>();for (int x : nums) {if (!set.add(x)) {return true;}}return false;} }

C#开发的自定义提示和对话框窗体 - 开源研究系列文章

上次开发了《LUAgent服务器端工具》&#xff0c;然后就开发了自定义的提示和对话框窗体&#xff0c;因为这个是无边框窗体&#xff0c;所以不使用默认的MessageBox了&#xff0c;界面美观并且用户体验更好一些。然后就写了此文&#xff0c;让其他读者能够使用或者复用此类库的代…

非对称加密算法原理与应用2——RSA私钥加密文件

作者:私语茶馆 1.相关章节 (1)非对称加密算法原理与应用1——秘钥的生成-CSDN博客 第一章节讲述的是创建秘钥对,并将公钥和私钥导出为文件格式存储。 本章节继续讲如何利用私钥加密内容,包括从密钥库或文件中读取私钥,并用RSA算法加密文件和String。 2.私钥加密的概述…

git pull拉取显示Already up-to-date,但文件并没有更新

1、问题&#xff1a; 使用git pull拉取远程仓库代码&#xff0c;显示更新成功&#xff08;Already up-to-date&#xff09;&#xff0c;但是本地代码没有更新 这是因为本地有尚未提交的更改&#xff0c;和远程代码有冲突导致无法更新 2、解决方法&#xff1a; 可以使用git s…

axios的使用,处理请求和响应,axios拦截器

1、axios官网 https://www.axios-http.cn/docs/interceptors 2、安装 npm install axios 3、在onMouunted钩子函数中使用axios来发送请求&#xff0c;接受响应 4.出现的问题&#xff1a; &#xff08;1&#xff09; 但是如果发送请求请求时间过长&#xff0c;回出现请求待处…

进程控制-exec函数

让父子进程来执行不相干的操作 能够替换进程地址空间的代码.text段 执行另外的程序&#xff0c;不需要创建额外的的地址空间 当前程序中调用另外一个应用程序 指定执行目录下的程序 int execl(const char *path, const char *arg&#xff0c;/* (char *) NULL */); /* pat…

3.python

闯关 3作业 本节关卡&#xff1a; 学习 python 虚拟环境的安装 Python 的基本语法 学会 vscode 远程连接 internstudio 打断点调试 python 程序

数据库管理-第216期 Oracle的高可用-01(20240703)

数据库管理216期 2024-07-03 数据库管理-第216期 Oracle的高可用-01&#xff08;20240703&#xff09;1 MAA简介2 MAA等级2.1 BRONZE2.2 SILVER2.3 GOLD2.4 PLATINUM 3 业务延续性总结 数据库管理-第216期 Oracle的高可用-01&#xff08;20240703&#xff09; 作者&#xff1a;…

cs224n作业4

NMT结构图&#xff1a;&#xff08;具体结构图&#xff09; LSTM基础知识 nmt_model.py&#xff1a; 参考文章&#xff1a;LSTM输出结构描述 #!/usr/bin/env python3 # -*- coding: utf-8 -*-""" CS224N 2020-21: Homework 4 nmt_model.py: NMT Model Penchen…