首页 > 其他分享 >多任务互斥与同步

多任务互斥与同步

时间:2023-09-15 09:11:21浏览次数:36  
标签:同步 int 信号量 互斥 mutex pthread sem 多任务

多任务互斥与同步

1. 互斥和同步概述

同步和互斥是用于解决如下两个问题:

1)在多任务操作系统中,同时运行的多个任务可能都需要访问/使用同一种资源。

2)多个任务之间有依赖关系,某个任务的运行依赖于另一个任务

互斥:

  • 一个公共资源同一时刻只能被一个进程或线程使用,多个进程或线程不能同时使用公共资源

  • POSIX标准中进程和线程同步和互斥的方法,主要有信号量好互斥锁两种方式

同步:

两个或两个以上的进程或线程在运行过程中协同步调,按预定的先后次序运行。

【注】同步是特殊的互斥

2. 互斥锁

2.1 互斥锁的概念

用于线程的互斥

mutex互斥锁是一种简单的加锁的方法来控制对共享资源的访问

互斥锁只有两种状态,即加锁(lock)和解锁(unlock)

互斥锁的操作流程如下

1)在访问共享资源前,对互斥锁进行加锁

2)在访问完成后释放互斥锁

3)对互斥锁进行加锁后,任何其他试图再次对互斥锁加锁的线程将会被阻塞,直到锁被释放

2.2 初始化互斥锁

不管多少个任务,如果是完成互斥,只需要一把锁

mutexpthread_mutex_t数据类型表示,在使用互斥锁前,必须先对它进行初始化

2.2.1 静态初始化

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER

2.2.2 动态初始化

#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *attr);

参数:

  • mutex:互斥锁地址。类型是pthread_mutex_t
  • attr:设置互斥量的属性,通常可采用默认属性,即可将attr设为NULL

返回值:成功返回0,失败返回非0

2.3 互斥锁上锁

对互斥锁上锁,若已经上锁,则调用者一直阻塞到互斥锁解锁【阻塞的】

#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);

返回值:成功返回0,失败返回非0

2.4 互斥锁上锁2

对互斥锁上锁,若已经上锁,则上锁失败,函数立即返回。【非阻塞的】

#include <pthread.h>
int pthread_mutex_trylock(pthread_mutex_t *mutex);

返回值:成功返回0,失败返回非0

2.5 互斥锁解锁

对指定的互斥锁解锁

#include <pthread.h>
int pthread_mutex_unlock(pthread_mutex_t *mutex);

返回值:成功返回0,失败返回非0

2.6 销毁互斥锁

销毁指定的一个互斥锁

#include <pthread.h>
int pthread_mutex_destroy(pthread_mutex_t *mutex);

返回值:成功返回0,失败返回非0

2.7 死锁

image-20230913134651879

如:两个锁使用错误,导致死锁

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

pthread_mutex_t mutex1,mutex2;
void *task1(void *data)
{
    char *taskName = (char *)data;
    pthread_mutex_lock(&mutex1);
    printf("%s 获取锁1成功,等待1秒之后获取锁2\n",taskName);
    sleep(1);
    pthread_mutex_lock(&mutex2);
    printf("%s 获取锁2成功\n",taskName);
    printf("%s\n",taskName);
    pthread_mutex_unlock(&mutex2);
    pthread_mutex_unlock(&mutex1);
    return NULL;
}
void *task2(void *data)
{
    char *taskName = (char *)data;
    pthread_mutex_lock(&mutex2);
    printf("%s 获取锁2成功,等待1秒之后获取锁1\n",taskName);
    sleep(1);
    pthread_mutex_lock(&mutex1);
    printf("%s 获取锁1成功\n",taskName);
    printf("%s\n",taskName);
    pthread_mutex_unlock(&mutex1);
    pthread_mutex_unlock(&mutex2);
    return NULL;
}
int main(int argc,char const *argv[])
{
    //动态初始化互斥锁
    pthread_mutex_init(&mutex1,NULL);
    pthread_mutex_init(&mutex2,NULL);
    pthread_t tid1,tid2;
    pthread_create(&tid1,NULL,task1,"hello");
    pthread_create(&tid2,NULL,task2,"good");
    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    //销毁锁
    pthread_mutex_destroy(&mutex1);
    pthread_mutex_destroy(&mutex2);
    return 0;
}

解决问题,一定要按顺序加锁与解锁

void *task1(void *data)
{
    char *taskName = (char *)data;
    pthread_mutex_lock(&mutex1);
    printf("%s 获取锁1成功, 等待1秒之后获取锁2\n", taskName);
    sleep(1);
    pthread_mutex_lock(&mutex2);
    printf("%s 获取锁2成功\n", taskName);
    printf("%s\n", taskName);
    pthread_mutex_unlock(&mutex2);
    pthread_mutex_unlock(&mutex1);
    return NULL;
}
void *task2(void *data)
{
    char *taskName = (char *)data;
    pthread_mutex_lock(&mutex1);
    printf("%s 获取锁1成功, 等待1秒之后获取锁2\n", taskName);
    sleep(1);
    pthread_mutex_lock(&mutex2);
    printf("%s 获取锁2成功\n", taskName);
    printf("%s\n", taskName);
    pthread_mutex_unlock(&mutex2);
    pthread_mutex_unlock(&mutex1);
    return NULL;
}

3. 读写锁

如果就两个任务,一个读,一个写,建议使用互斥锁。

如果3个或3个以上的任务,多个读或多个写,建议使用读写锁

3.1 读写锁的概述

读写锁(Read-Write-Lock)是一种并发控制机制,用于在多线程环境下对共享资源进行读写操作的同步和互斥。

读写锁允许多个线程同时对共享资源进行读取操作,但在有线程进行写入操作时,其他线程无法进行读取或写入操作,从而保证了对共享资源的安全访问

读写锁通常有两种状态:读取状态和写入状态。当没有线程进行写入操作时,多个线程可以同时获取读取锁,从而并发地读取共享资源。这样可以提高并发性能,因为读取操作不会修改共享资源,不会产生数据竞争。

当有线程获取写入锁时,其他线程无法获取读取锁或写入锁,从而实现了独占式的写入操作。这是为了保证数据的一致性和完整性,因为写入操作可能会修改共享资源,需要排他地进行

读写锁的基本特点如下:

  • 多个线程可以同时获取读取锁,实现并发的读取操作
  • 写入锁是独占的,当有线程获取写入锁时,其他线程无法获取读取锁或写入锁
  • 当有线程持有读取锁时,其他线程可以继续获取读取锁,但不能获取写入锁
  • 读取锁和写入锁之间的优先级关系可以根据具体的实现策略而定,如读优先或写优先

读写锁的使用场景通常是在读操作远远多于写操作的情况下,通过允许多个线程同时读取来提高并发性能。然而,需要注意的是,如果读操作的频率非常高,而写操作的频率很低,那么读写锁可能会导致写入操作的饥饿,即写入操作一直得不到执行。

3.2 读写锁的API

读写锁的类型:pthread_rwlock_t

3.2.1 动态初始化

#include <pthread.h>
int pthread_rwlock_init(pthread_rwlock_t *restrict_rwlock,const pthread_rwlockattr_t *restrict_attr);

参数:

  • rwlock:指向要初始化的读写锁指针
  • attr:读写锁的属性指针。如果attr为NULL则会使用默认的属性初始化读写锁,否则使用指定的attr初始化读写锁

3.2.2 静态初始化

pthread_rwlock_t my_rwlock = PTHREAD_RWLOCK_INITIALIZER;

这种方法等价于使用NULL指定的attr参数调用pthread_init()来完成动态初始化,不同之处在于PTHREAD_RWLOCK_INITIALIZER宏不进行错误检查

3.2.3 销毁读写锁

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

3.2.4 申请读锁

阻塞方式申请

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);

以阻塞方式在读写锁上获取读锁(读锁定)

如果没有写者持有该锁,并且没有写者阻塞在该锁上,则调用线程会获取读锁。

如果调用线程未获取读锁,则它将阻塞直到它获取了该锁。一个线程可以在一个读写锁上多次执行读锁定。

线程可以成功调用pthread_rwlock_rdlock()函数n次,但是之后该线程必须调用pthread_rwlock_unlock()函数n次才能解除锁定。

非阻塞方式申请

int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);

用于尝试以非阻塞的方式来在读写锁上获取读锁

如果有任何的写者持有该锁或有写者阻塞在该读写锁上,则立即失败返回。

3.2.5 申请写锁

阻塞方式申请

int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);

在读写锁上获取写锁(写锁定)

如果没有写者持有该锁,并且没有写者的读者持有该锁,则调用线程会获取写锁

如果调用线程未获取写锁,则它将阻塞直到它获取了该锁

非阻塞方式申请

int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);

用于尝试以非阻塞的方式来在读写锁上获取写锁

如果有任何的读者或写者持有该锁,则立即返回失败

3.2.6 释放读写锁

int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

无论是读锁或写锁,都可以通过此函数解锁

例:银行存取款

设计两个线程任务函数,一个完成取款,一个完成存款

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;

void *task1(void *data)
{
    int *money = (int *)data;
    //存款任务
    pthread_rwlock_wrlock(&rwlock);
    //查询余额
    printf("金额:%d\n",*money);
    sleep(1);
    
    //从键盘读取存入的金额
    printf("请输入存款金额:");
    fflush(stdout);
    int m;
    scanf("%d",&m);
    *money += m;
    //修改余额并打印结果
    printf("存款成功,余额为:%d\n",*money);
    pthread_rwlock_unlock(&rwlock);
    pthread_exit(NULL);
}

void *task2(void *data)
{
    int *money = (int*)data;
    //取款任务
    pthread_rwlock_wrlock(&rwlock);
    //查询余额
    printf("余额:%d\n",*money);
    sleep(1);
    //从键盘读取取出的金额
    printf("请输入取款金额:");
    fflush(stdout);
    int m;
    scanf("%d",&m);
    if(*money>=m)
    {
        *money-=m;
        //修改余额并打印结果
        printf("取款成功,余额为:%d\n",money);
    }
    else
    {
        printf("取款失败,余额不足\n");
    }
    pthread_rwlock_unlock(&rwlock);
    pthread_exit(NULL);
}
int main(int argc,char const *argv[])
{
    int money = 1000;//初始存款
    pthread_t tid1,tid2;
    pthread_create(&tid1,NULL,task1,&money);
    pthread_create(&tid2,NULL,task2,&money);
    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    pthread_rwlock_destroy(&rwlock);
    return 0; 
}

4. 条件变量

与互斥锁不同,条件变量是用来等待而不是用来上锁的,条件变量本身不是锁。

条件变量用来自动阻塞一个线程,直到某特殊情况发生为止,通常条件变量和互斥锁同时使用。

条件变量的两个动作:条件不满足,阻塞线程;当条件满足,通知阻塞的线程开始工作

条件变量的类型:pthread_cond_t

4.1 条件变量初始化

动态条件变量初始化

#include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);

attr:条件变量属性,通常为默认值,传NULL即可。

静态初始化条件变量

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

4.2 销毁条件变量

int pthread_cond_destroy(pthread_cond_t *cond);

4.3 等待条件满足

4.3.1 阻塞等待一个条件变量

int pthread_cond_wait(pthread_cond_t *restrict_cond,pthread_mutex_t *restrict_mutex);

功能:

  • a)阻塞等待条件变量cond(参1)满足
  • b)释放已掌握的互斥锁(解锁互斥量)相当于pthread_mutex_unlock(&mutex);
    • a)b)两步为一个原子操作
    • c)当被唤醒,pthread_cond_wait函数返回时,解除阻塞并重新申请获取互斥锁pthread_mutex_lock(&mutex);

参数:

  • cond:指向要初始化的条件变量指针
  • mutex:互斥锁

4.3.2 限时等待一个条件变量

int pthread_cond_timewait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex,
const struct timespec *restrict abstime);
  • cond:指向要初始化的条件变量指针
  • mutex:互斥锁
  • abstime:绝对时间

4.4 唤醒等待的线程

//唤醒至少一个阻塞在条件变量上的线程
int pthread_cond_signal(pthread_cond_t *cond);
//唤醒全部阻塞在条件变量上的线程
int pthread_cond_broadcast(pthread_cond_t *cond);

4.5 条件变量的工作原理

image-20230914085114341

4.6 案例:生产者与消费者

一个仓库刚开始有3个产品

如果生产者生产后进入仓库存放产品,消费者就不能进入仓库购买

如果消费者进入仓库购买产品,生产者就不能进入仓库存放产品

如果仓库的商品为0,消费者不能进入仓库购买

image-20230914134111815

如:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

//互斥锁和条件变量的初始化
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

void *producer_task(void *data)
{
    int *n=(int*)data;
    while(1)
    {
        pthread_mutex_lock(mutex);
        (*n)++;
        printf("生产线程(%ld)生产了%d产品\n",pthread_self(),*n);
        //发出通知,让等待消费的线程恢复(条件满足)
        pthread_cond_broadcast(&cond);
        pthread_mutex_unlock(&mutex);
        sleep(1);
    }
    pthread_exit(NULL);
}
void *consumer_task(void *data)
{
    int *n=(int*)data;
    while(1)
    {
        pthread_mutex_lock(mutex);
        while(*n==0)
        {
            pthread_cond_wait(&cond,&mutex);
        }
        printf("消费者(%ld)消费了%d产品\n",pthread_self(),*n);
        (*n)--;
        pthread_mutex_unlock(&mutex);
        sleep(1);
    }
    pthread_exit(NULL);
}
int main(int argc,char const *argv[])
{
    int num=3;//3个产品
    pthread_t threads[5];
    //创建2个生产线程
    for(int i=0;i<2;i++)
    {
        pthread_create(&threads[i],NULL,producer_task,&num);
    }
    //创建3个消费者线程
    for(int i=2;i<5;i++)
    {
        pthread_create(&threads[i],NULL,consumer_task,&num);
    }
    for(int i=0;i<5;i++)
    {
        pthread_join(threads[i],NULL);
    }
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);
    return 0;
}

5. 信号量

5.1 信号量的概念

信号量广泛用于进程或线程间的同步和互斥,信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。

当信号量值大于0时,则可以访问,否则将阻塞,PV原语是对信号量的操作,一次P操作使信号量减1,一次V操作使信号量加1。

信号量数据类型为:sem_t

信号量完成互斥

不管有多少个任务,只要是互斥,只要一个信号量,并且初始化1

image-20230915081010413

信号量用于同步

有几个任务就需要有几个信号量,先执行为任务的信号初始化为1,其他信号量初始化为0,所有任务P自己的信号,V下一个任务的信号量

image-20230915081137250

5.2 信号量的API

5.2.1 信号量的初始化

创建一个信号量并初始化它的值,一个无名信号量在被使用前必须先初始化

#include <semaphore.h>
int sem_init(sem_t *sem,int pshared,unsigned int value);

参数:

  • sem:信号量的地址
  • pshared:等于0,信号量在线程间共享(常用);不等于0,信号量在进程间共享。
  • value:信号量的初始值

返回值:成功0,失败-1

5.2.2 P操作

功能:将信号量减1,如果信号量的值为0则阻塞,大于0可以减1

int sem_wait(sem_t *sem);

功能:尝试将信号量减1,如果信号量的值为0不阻塞,立即返回,大于0减1

int sem_trywait(sem_t *sem);

返回值:成功0失败-1

5.2.3 V操作

将信号量的值加1并发出信号唤醒等待线程

int sem_post(sem_t *sem);

返回值:成功0失败-1

5.2.4 销毁信号量

int sem_destroy(sem_t *sem);

返回值:成功0失败-1

5.2.5 获取信号量的计数值

int sem_getvalue(sem_t *sem,int *sval);

银行案例信号量实现:先存款再取款,通过信号量实现同步线程操作

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <semaphore.h>

sem_t sem1,sem2;//信号量
void *task1(void *data)
{
    int *money=(int *)data;
    //存款任务
    sem_wait(&sem1);
    //查询余额
    printf("存款任务-余额:%d\n",*money);
    sleep(1);
    //从键盘读取存入的金额
    printf("请输入存款金额:");
    fflush(stdout);
    int m;
    scanf("%d",&m);
    *money += m;
    //修改余额并打印结果
    printf("存款成功,余额为:%d\n",*money);
    sem_post(&sem2);
    pthread_exit(NULL);
}
void *task2(void *data)
{
    int *money=(int *)data;
    //取款任务
    sem_wait(&sem2);
    //查询余额
    printf("余额:%d\n",*money);
    sleep(1);
    //从键盘读取取出的金额
    printf("请输入存款金额:");
    fflush(stdout);
    int m;
    scanf("%d",&m);
    if(*mony>=m)
    {
        *money-=m;
        //修改余额并打印结果
        printf("取款成功,余额为:%d\n",*money);
    }else{
        printf("取款失败,余额不足\n");
    }
    sem_post(&sem1);
    pthread_exit(NULL);
}
int main(int argc,char const *argv[])
{
    //初始化信号量
    sem_init(&sem1,0,1);//存款的信号量初始化
    sem_init(&sem2,0,0);//取款的信号量初始化
    int money = 1000;//存款
    pthread_t tid1,tid2;
    pthread_create(&tid1,NULL,task1,&money);
    pthread_create(&tid2,NULL,task2,&money);
    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    sem_destroy(&sem1);
    sem_destroy(&sem2);
    return 0;
}

6. 有名信号量

6.1 有名信号量概念

其实posix的信号有两种:

  • 1.无名信号量
  • 2.有名信号量

无名信号量一般用于线程间同步或互斥。而有名信号量一般用于进程间同步或互斥

6.2 创建有名信号量

创建一个信号量

#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
//信号量存在
sem_t *sem_open(const char *name,int oflag);
//信号量不存在
sem_t *sem_open(const char *name,int oflag,mode_t mode,unsigned int value);

参数:

  • name:信号量文件名
  • flagssem_open函数的行为标志,同open的flag
  • mode:文件权限(可读、可写、可执行)的设置
  • value:信号量初始值

返回值:成功返回信号量的地址,失败返回SEM_FAILED

6.3 信号量的关闭

int sem_close(sem_t *sem);

返回值:成功返回0,失败返回-1

6.4 信号量的删除

删除信号量的文件

int sem_unlink(const char *name);

返回值:成功0,失败-1

如:

#include <sys/stat.h>
#include <fcntl.h>
#include <semaphore.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

void printer(char *msg)
{
    while(*msg)
    {
        printf("%c",*msg++);
        fflush(stdout);
        sleep(1);
    }
}

父子进程间有名信号的互斥

int main()
{
    sem_t *sem = sem_open("mysem",O_CREAT|O_RDWR,0666,1);
    int pid = fork();
    if(pid==0)
    {
        sem_wait(sem);
        printer("dijun666");
        sem_post(sem);
        _exit(0);
    }
    else if(pid>0)
    {
        sem_wait(sem);
        printer("changxihe666");
        sem_post(sem);
        wait(NULL);
        printf("\n--over--\n");
        sem_close(sem);
        sem_unlink("mysem");//删除信号量文件
    }
    return 0;
}

父子进程,有名信号量的同步

int main()
{
    sem_t *sem1 = sem_open("mysem1",O_CREAT|O_RDWR,0666,1);
    sem_t *sem2 = sem_open("mysem2",O_CREAT|O_RDWR,0666,0);
    int pid = fork();
    if(pid==0)
    {
        sem_wait(sem1);
        printer("dijun666");
        sem_post(sem2);
        _exit(0);
    }
    else if(pid>0)
    {
        sem_wait(sem2);
        printer("changxihe666");
        sem_post(sem1);
        wait(NULL);
        printf("\n--over--\n");
        sem_close(sem1);
        sem_close(sem2);
        sem_unlink("mysem1");//删除信号量文件
        sem_unlink("mysem2");
    }
    return 0;
}

无血缘关系的进程的互斥

int main()
{
    //打开信号量文件
    sem_t *sem = sem_open("mysem",O_CREAT|O_RDWR,0666,1);
    sem_wait(sem);
#ifdef DIJUN
    printer("dijun666\n");
#else 
    printer("changxihe888\n");
#endif
    sem_post(sem);
    sem_close(sem);
    return 0;
}

无血缘关系的进程的同步

int main()
{
       //打开信号量文件
    sem_t *sem1 = sem_open("mysem1",O_CREAT|O_RDWR,0666,1);
      sem_t *sem2 = sem_open("mysem2",O_CREAT|O_RDWR,0666,1);
#ifdef DIJUN
    sem_wait(sem2);
    printer("dijun666\n");
    sem_post(sem1);
#else
    sem_wait(sem1);
    printer("changxihe888\n");
    sem_post(sem2);
#endif
    sem_close(sem1)
    sem_close(sem2);
    return 0;
}

标签:同步,int,信号量,互斥,mutex,pthread,sem,多任务
From: https://www.cnblogs.com/dijun666/p/17704050.html

相关文章

  • ntp时间同步
    1、检查系统是否安装了NTP包(linux系统一般自带NTP4.2)没有安装我们直接使用yum命令在线安装:yuminstallntpsystemctlenablentpd开机启动ntpsystemctlstartntpd启动服务systemctlstopntpd停止服务systemctlstatusntpd查看服务状态2、NTP服务端配置文件编辑vim/et......
  • 同步异步 阻塞非阻塞
    同步异步描述的事任务的提交方式 描述的事一段代码或者函数同步:任务提交后,原地等待任务的返回结果,等待的过程中不做任何事(干等)程序层面上表现出来的感觉就是卡住了例子:importtime deffunc():time.sleep(3)print('helloworld')  if__name++=='__main__......
  • 使用python进行数据同步,根据游标的方式
    使用python进行数据同步,根据游标的方式一、介绍:数据同步分为好几种,比如根据最大id排序,其次才是根据游标进行排序。为什么不使用最大id进行数据同步?如下:因为如果数据表中的数据是按照降序排序的,那么每次根据最大id取1000条数据的方式也是一种可行的分页查询方式。这种方式的优......
  • 论文研读_大规模多目标优化问题的两阶段多任务转换框架
    本文的创新点在哪里采用两阶段框架的方式来处理大规模多目标优化问题第一阶段,采用多任务优化策略和双向搜索策略,它将多目标问题改编为决策空间中的多任务问题,以增强收敛性利用双向搜索策略与MFEA相结合,将多目标问题转化为多任务问题第二阶段,提出的算法将多任务优化应......
  • 修改了虚拟机名称后,数据存储上的文件名称如何同步修改
    更改虚拟机名称虚拟机的名称在虚拟机所在的文件夹中必须是唯一的。如果将某虚拟机移至其他数据存储文件夹或移至具有相同名称的现有虚拟机的主机,则可以更改该虚拟机的名称以使其保持唯一。 更改某虚拟机的名称时,应更改 vCenterServer 清单中用于标识该虚拟机的名称。该操作......
  • 火狐浏览器的书签丢失了怎么办?书签同步失败怎么办?
    参考:http://mzl.la/1BAQULj 您对Firefox做的所有更改,比如您的主页、工具栏、扩展、保存的密码以及书签等,都被保存在一个特别的文件夹中,称为用户配置文件夹。您的用户配置文件夹和Firefox的程序文件并不在一起,这样一旦Firefox出现问题,您的所有信息仍旧是安全的。也就是说,......
  • Terraform 状态不同步处理
    背景在使用Terraform创建TencentCloudTKE的时候,手贱把nodepool删掉了。导致执行destroy,plan都会报错。│Error:[TencentCloudSDKError]Code=InternalError.UnexpectedInternal,Message=relatednodepoolqueryerr(getnodepoolfailed:[E501001DBRecordNotFou......
  • buildroot 构建根文件系统(8)添加网络时间同步
    一、开发背景构建最小系统后成功运行后,时间都是从初始化时间开始计算,形如:ThuJan 109:57:55UTC1970二、开发需求开机联网后自动从网络中获取最新的时间,同步到系统中三、开发环境LinuxUbuntu4.15.0-65-generic+buildroot-2023.02.3+i.mx6d(cortex-A9)......
  • 25进程/join方法/互斥锁
    代码创建进程"""创建进程的方式有哪些1.鼠标双击桌面一个应用图标2.代码创建创建进程的本质:在内存中申请一块内存空间用于运行相应的程序代码"""#第一种创建进程的方式》》对象#frommultiprocessingimportProcess#importtime###deftask(name)......
  • ES 数据同步
    Elasticsearch中的数据来组与mysql,因此mysql数据发生改变时,elasticsearch也必须跟着改变,这个就是elasticsearch与mysql之间的数据同步。在微服务中,负责酒店管理(操作mysql)的业务与负责酒店搜索(操作elasticsearch)的业务可能在两个不同的微服务上,数据同步该如何实现那?方......