linux和C++中的 线程同步与线程安全 对比

线程同步与线程安全

  • 线程
    • 进程与线程的区别
    • 并发和并行的区别
    • linux线程常用接口函数
      • 函数定义
      • 函数使用
  • 多线程理解 线程同步
    • 五个线程同时启动,每一个循环打印3次
    • 五个线程,每一个循环1000 结果是<=5000
      • 代码和测试结果
      • 测试结果分析
      • 可以用信号量和互斥锁解决该问题
      • 互斥锁相同于初始值为1的信号量
    • linux下用信号量实现打印abc abc...
      • 图示理解
      • 编程实现
    • C++用互斥锁和条件变量实现三个线程打印abcabcabc
      • 编程实现
      • cv.wait分析
    • C++实现打印1234...cv.wait 结合lambda表达式
  • 线程安全
    • 用一个主线程,一个子线程,做同一个事情
    • linux读写锁
      • 特点
      • 编程实例
    • linux条件变量
      • 定义作用
      • 条件变量使用实例
    • 线程与fork
  • 死锁

线程

进程:一个正在运行的程序
线程:进程内部的一条实行路径

进程与线程的区别

◼ 进程是资源分配的最小单位,线程是 CPU 调度的最小单位
◼ 进程有自己的独立地址空间,线程共享进程中的地址空间
◼ 进程的创建消耗资源大,线程的创建相对较小
◼ 进程的切换开销大,线程的切换开销相对较小

并发和并行的区别

并发:cpu只有一个核,其实就是交替执行,宏观上是一起的,微观上是交替
并行:看cup的核数,多个核就可以并行,微观上是一起执行的
在这里插入图片描述
线程可以共享同一个进程的资源 ,代码 数据 堆 栈,每个线程互不干扰
在这里插入图片描述

linux线程常用接口函数

函数定义

pthread_create()用于创建线程

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg)

thread: 接收创建的线程的 ID
attr: 指定线程的属性
start_routine: 指定线程函数
arg: 给线程函数传递的参数
成功返回 0, 失败返回错误码

pthread_exit()退出线程

int pthread_exit(void *retval);

retval:指定退出信息

pthread_join()
等待 thread 指定的线程退出,线程未退出时,该方法阻塞 类似wait
retval:接收 thread 线程退出时,指定的退出信息

int pthread_join(pthread_t thread, void **retval)

一般让主线程最后结束,子线程先结束没有影响,主线程可以等待 pthread_join(id,(void**)&s); s指向"fun over\n"字符串常量

函数使用

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
void *thread_fun(void *arg)
{
    for (int i = 0; i < 5; i++)
    {
        printf("fun run\n");
        sleep(1);
    }
    pthread_exit("fun over\n");
}
int main()
{
    pthread_t id;
    pthread_create(&id, NULL, thread_fun, NULL);

    for (int i = 0; i < 5; i++)
    {
        printf("main fun\n");
        sleep(1);
    }
    char *s = NULL;
    pthread_join(id, (void **)&s);
    printf("s=%s\n", s);
    exit(0);
}

在这里插入图片描述

多线程理解 线程同步

五个线程同时启动,每一个循环打印3次

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
void *thread_fun(void *arg)
{
    int index = *(int *)arg;
    for (int i = 0; i < 3; i++)
    {
        printf("index=%d\n", index);
        sleep(1);
    }
    pthread_exit("fun over\n");
}
int main()
{
    pthread_t id[5];
    for (int i = 0; i < 5; i++)
    {
        pthread_create(&id[i], NULL, thread_fun, (void *)&i);
    }
    for (int i = 0; i < 5; i++)
    {
        pthread_join(id[i], NULL);
    }

    exit(0);
}

在这里插入图片描述
int index = *(int *)arg; 主线程中i还在变化,不会专门等index获取i,i这里类似全局变量,多个线程一起执行,当两个线程一起执行,可能获取的i的值就相同,时间差很小,可能获取的i的值就相同

改动一下

int index = (int)arg;

在这里插入图片描述
3部分,每个子线程执行三次,说明5个子线程都同时执行,打印i的值,所以01234 但是每个子线程的速度不同,所以不一定是01234

或者通过结构体传参,将该时刻的i的值保存到结构体里,拷贝一份,就不会改变了

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

struct arg_node{
    int index;
};

void *thread_fun(void *arg)
{
    struct arg_node *p = (struct arg_node *)arg;
    for (int i = 0; i < 3; i++)
    {
        
        printf("index=%d\n", p->index);
        sleep(1);
    }
    free(p);
    pthread_exit("fun over\n");
}
int main()
{
    pthread_t id[5];
    for (int i = 0; i < 5; i++)
    {
        struct arg_node *p = (struct arg_node *)malloc(sizeof(struct arg_node));
        p->index=i;
        pthread_create(&id[i], NULL, thread_fun, (void *)p);
    }
    for (int i = 0; i < 5; i++)
    {
        pthread_join(id[i], NULL);
    }

    exit(0);
}

在这里插入图片描述

五个线程,每一个循环1000 结果是<=5000

代码和测试结果

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
int g_index = 1;

void *fun(void *arg)
{
    for (int i = 0; i < 1000; i++)
    {
        printf("g_index=%d\n", g_index++);
    }
}

int main()
{
    pthread_t id[5];
    for (int i = 0; i < 5; i++)
    {
        pthread_create(&id[i], NULL, fun, NULL);
    }

    for (int i = 0; i < 5; i++)
    {
        pthread_join(id[i], NULL);
    }
}

在这里插入图片描述

测试结果分析

分析一下:如果有两条不同的子线程同时执行,i++ i=1时,第一个寄存器读到1,还没来得及++,没来的及写回去,第二个寄存器同时读过来,此时还是1,两个寄存器都加成2了,几乎同一时间写入,此时,i=2 原本应该是i=3 所以出现数据小于等于5000,看同时++了几次,出现一次,小一次

这是系统有多个处理器,如果系统只有一个处理器,就无法做到并行,只能交替执行,即并发执行,那样的话,五个子线程就无法一起执行,只能时间片轮转执行,结果就是5000
一个处理器就类型一个商场,每次只能一个人进去,只有一个试衣间,五个人轮着进去,四个处理器就类似一个商场,每次可以4一个同时进去,但是,试衣间只有一间,所以加锁,谁先抢到就先进试衣间

可以用信号量和互斥锁解决该问题

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

sem_t sem;
int g_index = 1;

void *fun(void *arg)
{
    for (int i = 0; i < 1000; i++)
    {
        sem_wait(&sem);
        printf("g_index=%d\n", g_index++);
        sem_post(&sem);
    }
}

int main()
{
    sem_init(&sem,0,1);
    pthread_t id[5];
    for (int i = 0; i < 5; i++)
    {
        pthread_create(&id[i], NULL, fun, NULL);
    }

    for (int i = 0; i < 5; i++)
    {
        pthread_join(id[i], NULL);
    }
    sem_destroy(&sem);
    exit(1);
}

这样打印的都是5000

互斥锁相同于初始值为1的信号量

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

//sem_t sem;
pthread_mutex_t mutex;
int g_index = 1;

void *fun(void *arg)
{
    for (int i = 0; i < 1000; i++)
    {
        //sem_wait(&sem);
        pthread_mutex_lock(&mutex);
        printf("g_index=%d\n", g_index++);
        //sem_post(&sem);
        pthread_mutex_unlock(&mutex);
    }
}

int main()
{
    //sem_init(&sem,0,1);
    
    pthread_mutex_init(&mutex,NULL);
    pthread_t id[5];
    for (int i = 0; i < 5; i++)
    {
        pthread_create(&id[i], NULL, fun, NULL);
    }

    for (int i = 0; i < 5; i++)
    {
        pthread_join(id[i], NULL);
    }
    //sem_destroy(&sem);
    pthread_mutex_destroy(&mutex);
    exit(1);
}

linux下用信号量实现打印abc abc…

图示理解

在这里插入图片描述

编程实现

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
sem_t sem_a;
sem_t sem_b;
sem_t sem_c;

void *funa(void *arg)
{
    for (int i = 0; i < 5; i++)
    {
        sem_wait(&sem_a);
        printf("A");
        fflush(stdout);
        sleep(1);
        sem_post(&sem_b);
    }
}

void *funb(void *arg)
{
    for (int i = 0; i < 5; i++)
    {
        sem_wait(&sem_b);
        printf("B");
        fflush(stdout);
        sleep(1);
        sem_post(&sem_c);
    }
}

void *func(void *arg)
{
    for (int i = 0; i < 5; i++)
    {
        sem_wait(&sem_c);
        printf("C");
        fflush(stdout);
        sleep(1);
        sem_post(&sem_a);
    }
}
int main()
{
	//信号量初始化
    sem_init(&sem_a, 0, 1);
    sem_init(&sem_b, 0, 0);
    sem_init(&sem_c, 0, 0);

    pthread_t id1, id2, id3;
    pthread_create(&id1, NULL, funa, NULL);
    pthread_create(&id2, NULL, funb, NULL);
    pthread_create(&id3, NULL, func, NULL);

    pthread_join(id1, NULL);
    pthread_join(id2, NULL);
    pthread_join(id3, NULL);

    sem_destroy(&sem_a);
    sem_destroy(&sem_b);
    sem_destroy(&sem_c);

    exit(0);
}

在这里插入图片描述

C++用互斥锁和条件变量实现三个线程打印abcabcabc

编程实现

#include<iostream>
#include<vector>
#include<time.h>
#include<mutex>
#include<condition_variable>
#include<thread>
using namespace std;
const int n = 10;
int tag = 1;
std::mutex mtx;
std::condition_variable cv;
void funa() {
	unique_lock<std::mutex>lock(mtx);
	for (int i = 0; i < n; i++) {
		while (tag != 1) {
			cv.wait(lock);
		}
		printf("funa:%c\n", 'a');
		tag = 2;
		cv.notify_all();
	}
}
void funb() {
	unique_lock<mutex>lock(mtx);
	for (int i = 0; i < n; i++) {
		while (tag != 2) {
			cv.wait(lock);
		}
		printf("funb:%c\n", 'b');
		tag = 3;
		cv.notify_all();
	}
}
void func() {
	unique_lock<mutex>lock(mtx);
	for (int i = 0; i < n; i++) {
		while (tag != 3) {
			cv.wait(lock);
		}
		printf("func:%c\n", 'c');
		tag = 1;
		cv.notify_all();
	}
}
int main() {
	std::thread tha(funa);
	std::thread thb(funb);
	std::thread thc(func);
	tha.join();
	thb.join();
	thc.join();
	return 0;
}

在这里插入图片描述

cv.wait分析

在这里插入图片描述
cv.wait()条件变量的等待函数有4步:
1释放已拥有的互斥锁 即解锁
2 将当前的线程放入条件变量的等待队列中
3唤醒 等待其他线程来唤醒,收到通知,将线程移动到互斥锁的等待队列中
4.获锁 互斥锁队列中,各个线程抢占互斥锁,抢到锁的,到达就绪状态

此时,cv.wait()就可以返回了,继续执行while循环
加while循环的目的是防止被虚假唤醒

互斥锁队列中,想要到达就绪状态,必须获得锁
cv.notify_one():只唤醒等待队列中的第一个线程,不存在锁争用,所以能够立即获得锁。其余的线程不会被唤醒,需要等待再次调用cv.notify_one()或者cv.notify_all()。
cv.notify_all():唤醒等待队列中的所有线程。放入互斥锁队列

C++实现打印1234…cv.wait 结合lambda表达式

//两个线程    一个打印奇数 一个打印偶数
const int n = 100;
int i = 1;
std::mutex mtx;
std::condition_variable cv;
void funa()	// 1 
{
	std::unique_lock<std::mutex>lock(mtx);
	while (i <= n)
	{
		//while (i % 2 != 1)
		//{
		//			cv.wait(lock);
		//}
		cv.wait(lock, [&]()->bool {return i % 2 == 1; });
		if (i <= n) {

			printf("funa: %d \n", i);
		}
		++i;
		cv.notify_one();
	}
}
void funb()
{
	std::unique_lock<std::mutex> lock(mtx);
	while (i <= n)
	{
		while (i % 2 != 0)
		{
			cv.wait(lock);
		}
		if (i <= n) {

			printf("funb: %d \n", i);
		}
		++i;
		cv.notify_one();
	}
}

int main()
{
	std::thread tha(funa);
	std::thread thb(funb);

	tha.join();
	thb.join();

	return 0;
}

在这里插入图片描述
lambda表达式是 返回为真,不执行wait 为假执行wait ,当wait一个过程结束后,不直接返回,而是再看返回值,为真就跳出,为假,就继续执行wait

//while (i % 2 != 1)
//{
//	cv.wait(lock);
//}
cv.wait(lock, [&]()->bool {return i % 2 == 1; });

线程安全

保证线程的安全:同步(使用信号量 互斥锁),使用线程安全的函数

用一个主线程,一个子线程,做同一个事情

用一个主线程,一个子线程,做同一个事情,用strtok();分割字符串
可以参考这篇博客:strtok线程安全

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
void *fun(void *arg)
{
    char buff[] = {"1 2 3 4 5 6 7 8"};
   
    char *s = strtok(buff, " ");
    while (s != NULL)
    {
        printf("fun s=%s\n", s);
        sleep(1);
        s = strtok(NULL, " ");
    }
}

int main()
{
    pthread_t id;
    pthread_create(&id, NULL, fun, NULL);

    char str[] = {"a b c d e f g h"};
    
    char *s = strtok(str, " ");
    while (s != NULL)
    {
        printf("main s=%s\n", s);
        sleep(1);
        s = strtok(NULL, " ");
    }

    pthread_join(id, NULL);
    exit(0);
}

strtok();到达字符串尾部,‘\0’ ,s就会返回nullptr

在这里插入图片描述
strtok() 函数内部有一个静态变量,用来记录当时指向的位置,多个函数调用strtok()访问的是同一个空间 ,这里两个线程,而静态变量只有一个空间,只能记住一个str或者buff,所以是谁后赋值就记住谁
所以主线程先调用strtok 传入str 打印a 记住b 子线程启动又调用strtok 就会更改静态变量指向的位置,指向1
记住2 当主线程在此打印时,此时静态指针指向的就是2 记录3 回到子线程 打印3

加锁不能解决该问题,可以使用线程安全版本,strtok_r()多了一个指针,记录当前指向的位置,

void *fun(void *arg)
{
    char buff[] = {"1 2 3 4 5 6 7 8"};
    char *ptr = NULL;
    char *s = strtok_r(buff, " ", &ptr);
    while (s != NULL)
    {
        printf("fun s=%s\n", s);
        sleep(1);
        s = strtok_r(NULL, " ", &ptr);
    }
}

int main()
{
    pthread_t id;
    pthread_create(&id, NULL, fun, NULL);

    char str[] = {"a b c d e f g h"};
    char *ptr = NULL;
    char *s = strtok_r(str, " ", &ptr);
    while (s != NULL)
    {
        printf("main s=%s\n", s);
        sleep(1);
        s = strtok_r(NULL, " ", &ptr);
    }

    pthread_join(id, NULL);
    exit(0);
}

`在这里插入图片描述

linux读写锁

特点

一个线程读的时候,其他线程也可以读,但是不能写,一个线程写的话,必须等其他线程读完,开始写,写的时候,其他线程不能读,也不能写
写读不能交替,但是两读可以交替
如果用互斥锁,两读之前也不能交替,必须等一个线程读完,另一个才能读

编程实例

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

pthread_rwlock_t lock;

void *fun1(void *arg)
{
    for (int i = 0; i < 20; i++)
    {
        pthread_rwlock_rdlock(&lock);
        printf("fun1  read start --\n");
        int n = rand() % 3;
        sleep(n);
        printf("fun1 read end----\n");
        pthread_rwlock_unlock(&lock);

        n = rand() % 3;
        sleep(n);
    }
}
void *fun2(void *arg)
{
    for (int i = 0; i < 20; i++)
    {
        pthread_rwlock_rdlock(&lock);
        printf("fun2  read start --\n");
        int n = rand() % 3;
        sleep(n);
        printf("fun2 read end----\n");
        pthread_rwlock_unlock(&lock);

        n = rand() % 3;
        sleep(n);
    }
}
void *fun3(void *arg)
{
    for (int i = 0; i < 10; i++)
    {
        pthread_rwlock_wrlock(&lock);
        printf("-----------fun3  write start --\n");
        int n = rand() % 3;
        sleep(n);
        printf("-----------fun3  write end --\n");
        pthread_rwlock_unlock(&lock);

        n = rand() % 3;
        sleep(n);
    }
}
int main()
{
    pthread_rwlock_init(&lock, NULL);
    pthread_t id1, id2, id3;
    pthread_create(&id1, NULL, fun1, NULL);
    pthread_create(&id2, NULL, fun2, NULL);
    pthread_create(&id3, NULL, fun3, NULL);

    pthread_join(id1, NULL);
    pthread_join(id2, NULL);
    pthread_join(id3, NULL);
    pthread_rwlock_destroy(&lock);

    exit(0);
}

在这里插入图片描述

linux条件变量

定义作用

如果说互斥锁是用于同步线程对共享数据的访问的话,那么条件变量则是用于在线程之间同步共享数据的值。条件变量提供了一种线程间的通知机制当某个共享数据达到某个值的时候,唤醒等待这个共享数据的线程。

线程会先执行wait,把自己放到条件变量的等待队列中,然后条件满足再唤醒

条件变量使用实例

有两个线程,都在等待键盘上的输入,获取数据,一旦从键盘获取了数据,就认为条件满足,就唤醒一个线程,打印该数据,当然一次只用一个线程做这个事情,当在缓冲区输入end 唤醒两个线程,让其判断用户想退出,各自结束各自的线程

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

pthread_cond_t cond;
pthread_mutex_t mutex;

char buff[128] = {0}; // 共享的数据

void *funa(void *arg)
{
    while (1)
    {
        // 等待数据可用(阻塞),被唤醒后解除阻塞
        pthread_mutex_lock(&mutex);
        pthread_cond_wait(&cond, &mutex);
        pthread_mutex_unlock(&mutex);
        if (strncmp(buff, "end", 3) == 0)
        {
            break;
        }
        printf("funa buff=%s\n", buff);
    }
}
void *funb(void *arg)
{
    while (1)
    {
        // 等待数据可用(阻塞),被唤醒后解除阻塞
        pthread_mutex_lock(&mutex);
        pthread_cond_wait(&cond, &mutex);
        pthread_mutex_unlock(&mutex);
        if (strncmp(buff, "end", 3) == 0)
        {
            break;
        }
        printf("funb buff=%s\n", buff);
    }
}

int main()
{
    pthread_t ida, idb;
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond, NULL);
    pthread_create(&ida, NULL, funa, NULL);
    pthread_create(&idb, NULL, funb, NULL);

    while (1)
    {
        fgets(buff, 128, stdin);

        if (strncmp(buff, "end", 3) == 0)
        {
            // 唤醒所有线程
            pthread_cond_broadcast(&cond);
            break;
        }
        else
        {
            // 只唤醒一个线程
            pthread_cond_signal(&cond);
        }
    }

    pthread_join(ida, NULL);
    pthread_join(idb, NULL);

    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);
    exit(0);
}

pthread_cond_wait(&cond, &mutex);

两个子线程同时移动到等待队列中,最后再出来,这里有个问题,两个不能同时进,需要互斥进入唤醒的时候希望没有线程正在进或者出队列
所以先加锁,再进入等待队列中,wait里面会解锁,
唤醒的时候,需要先在内部加锁,出来后再解锁

查看线程 一个主线程,两个子线程

ps -eLf | grep main

在这里插入图片描述

在这里插入图片描述

线程与fork

无论fork前有多少条执行路径,fork后,子进程只有一条执行路径 到底是哪个线程,由fork所在的位置决定,fork在主进程主线程中,子进程只启动一条执行路径,就是主进程中的主线程,fork在主进程子线程中,子进程只启动一条 执行路径,就是主进程中的子线程,
fork是复制资源,父进程啥资源子进程都有,只是fork后只启动一条执行路径

void*fun(void*arg){
    for (int i = 0; i < 5; i++)
    {
        printf("fun run  pid=%d\n",getpid());
        sleep(1);
    }
}
int main(){
    pthread_t id;
    pthread_create(&id,NULL,fun,NULL);
    fork();
    for(int i=0;i<5;i++){
        printf("main run  pid=%d\n",getpid());
        sleep(1);
    }
    pthread_join(id,NULL);
    exit(0);
}

在这里插入图片描述

void*fun(void*arg){
    fork();
    for (int i = 0; i < 5; i++)
    {
        printf("fun run  pid=%d\n",getpid());
        sleep(1);
    }
}
int main(){
    pthread_t id;
    pthread_create(&id,NULL,fun,NULL);
    
    for(int i=0;i<5;i++){
        printf("main run  pid=%d\n",getpid());
        sleep(1);
    }
    pthread_join(id,NULL);
    exit(0);
}

在这里插入图片描述

死锁

死锁是多线程并发编程中一种常见的问题,它发生在两个或多个线程相互等待对方持有的资源导致无法继续执行的情况。
可用用

void funa() {
	std::unique_lock<std::mutex>lock1(mtx1);
	cout << "funa thread get mtx1" << endl;
	std::this_thread::sleep_for(std::chrono::milliseconds(1000));
	std::unique_lock<std::mutex>lock2(mtx2);
	cout << "funa thread get mtx2" << endl;
}
void funb() {
	std::unique_lock<std::mutex>lock2(mtx2);
	cout << "funb thread get mtx2" << endl;
	std::this_thread::sleep_for(std::chrono::milliseconds(1000));
	std::unique_lock<std::mutex>lock1(mtx1);
	cout << "funb thread get mtx1" << endl;
}
int main() {
	thread tha(funa);
	thread thb(funb);
	tha.join();
	thb.join();
	
	return 0;
}

在这里插入图片描述

std::lock(lock1, lock2);
如果只获得一个锁,就会释放已经获得的锁,防止死锁问题

#include<iostream>
#include<mutex>
#include<condition_variable>
#include<thread>
using namespace std;

std::mutex mtx1;
std::mutex mtx2;
void funa() {
	std::unique_lock<std::mutex>lock1(mtx1,std::defer_lock);
	std::unique_lock<std::mutex>lock2(mtx2, std::defer_lock);
	std::this_thread::sleep_for(std::chrono::milliseconds(1000));
	std::lock(lock1, lock2);
	cout << "funa thread get mtx1" << endl;
	cout << "funa thread get mtx2" << endl;
}
void funb() {
	std::unique_lock<std::mutex>lock1(mtx1, std::defer_lock);
	std::unique_lock<std::mutex>lock2(mtx2, std::defer_lock);
	std::this_thread::sleep_for(std::chrono::milliseconds(1000));
	std::lock(lock1, lock2);
	cout << "funb thread get mtx1" << endl;
	cout << "funb thread get mtx2" << endl;
}
int main() {
	thread tha(funa);
	thread thb(funb);
	tha.join();
	thb.join();
	
	return 0;
}

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

一文走进时序数据库性能测试工具 TSBS

一、背景 在物联网、车联网等时序数据场景中&#xff0c;数据的高速写入能力至关重要&#xff0c;会对产品方案的可用性、可靠性和扩展性产生影响。 以物联网为例&#xff0c;当面临千万甚至上亿设备、平均每个设备采集几十个到几百个指标时&#xff0c;每秒生成的数据将达到…

vue3 setup+Taro3 调用原生小程序自定义年月日时分多列选择器,NutUI改造

vue3 setupTaro3 调用原生小程序自定义年月日时分多列选择器&#xff0c;NutUI改造 NutUI 有日期时间选择器&#xff0c;但是滑动效果太差&#xff0c;卡顿明显。换成 原生小程序 很顺畅 上代码&#xff1a; <template><view><pickermode"multiSelector&…

Grafana+Prometheus技术文档-进阶使用-监控spring-boot项目

阿丹&#xff1a; 之前已经实现了使用Prometheus来对服务器进行了监控和仪表盘的创建&#xff0c;现在就需要对这些监控方法使用在spring-boot中去。 实现思路&#xff1a; 1、集成Actuator 2、加入Prometheus的依赖 3、配置开放端口、以及开放监控 4、配置Prometheus中的配置…

Linux命令200例:tree用于以树状结构显示文件和目录

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;全栈领域新星创作者✌。CSDN专家博主&#xff0c;阿里云社区专家博主&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &…

【Terraform学习】保护敏感变量(Terraform配置语言学习)

实验步骤 创建 EC2 IAM 角色 导航到IAM 在左侧菜单中&#xff0c;单击角色 。单击创建角色该按钮以创建新的 IAM 角色。 在创建角色部分&#xff0c;为角色选择可信实体类型&#xff1a; AWS 服务 使用案例:EC2 单击下一步 添加权限&#xff1a;现在&#xff0c;您可以看到…

穿越未来:探索虚拟现实科技的未来前景

虚拟现实&#xff08;Virtual Reality&#xff0c;简称VR&#xff09;科技&#xff0c;正如一颗崭新的明星&#xff0c;迅猛崛起&#xff0c;为人类带来前所未有的体验和想象空间。随着科技的飞速发展&#xff0c;VR 科技的未来充满了无限的可能性&#xff0c;正将我们引向一个…

VUE+ElementUI的表单验证二选一必填项,并且满足条件后清除表单验证提示

上代码 <el-form-item label"出库单号" prop"ecode" ref"ecode" :rules"rules.ecode"><el-input v-model"queryParams.ecode" placeholder"出库单号和出库箱号至少填写一项" clearable style"width…

SpringBoot启动报错:java: 无法访问org.springframework.boot.SpringApplication

报错原因&#xff1a;jdk 1.8版本与SpringBoot 3.1.2版本不匹配 解决方案&#xff1a;将SpringBoot版本降到2系列版本(例如2.5.4)。如下图&#xff1a; 修改版本后切记刷新Meavn依赖 然后重新启动即可成功。如下图&#xff1a;

Linux如何开启指定端口号

本文已收录于专栏 《运维》 目录 概念说明防火墙端口号 提供服务具体分类具体操作防火墙操作端口号操作 总结提升 概念说明 防火墙 防火墙是一种网络安全设备或软件&#xff0c;用于监控和控制网络流量&#xff0c;保护网络免受恶意攻击和未经授权的访问。防火墙可以根据预定义…

MySQL 数据类型总结

整形数据类型 1字节 8bit 2^8256

VBA_MF系列技术资料1-152

MF系列VBA技术资料 为了让广大学员在VBA编程中有切实可行的思路及有效的提高自己的编程技巧&#xff0c;我参考大量的资料&#xff0c;并结合自己的经验总结了这份MF系列VBA技术综合资料&#xff0c;而且开放源码&#xff08;MF04除外&#xff09;&#xff0c;其中MF01-04属于定…

iOS问题记录 - Xcode 15安装低版本iOS模拟器(持续更新)

文章目录 前言开发环境问题描述问题分析1. 定位问题2. 逆向分析2.1. IDA Free2.2. Hopper Disassembler Demo 3. 模拟器日志4. supportedArchs 解决方案最后 前言 最近新需求很多&#xff0c;项目改动很大&#xff0c;开发完成后想测一遍在低版本iOS系统上的兼容性&#xff0c…

shell脚本安装nginx

shell脚本原理 以删除桌面文件的脚本为例&#xff0c;执行脚本后&#xff0c;shell脚本将代码给内核&#xff0c;内核读取后执行命令&#xff0c;如果shell脚本也在桌面上&#xff0c;执行后这个脚本文件也会被删除。 变量 echo $SHELL$符表示SHELL是一个变量&#xff0c;变量…

Vc - Qt - 绘制绿色矩形

要在Qt中绘制一个绿色矩形&#xff0c;您需要创建一个自定义的QWidget或QGraphicsView类&#xff0c;在其绘制事件中使用QPainter来绘制形状。 以下是一个简单的示例&#xff0c;演示如何在QWidget中绘制一个绿色矩形&#xff1a; #include <QWidget> #include <QPain…

NLP 时事和见解【2023】

一、说明 AI的新闻当然不是即时的&#xff0c;但作为趋势和苗头&#xff0c;我们不得不做出自己的决定。比如&#xff0c;一些软件的支持是否持续&#xff0c;哪些现成的软件将不再使用&#xff0c;等等。 图片来自中途 以下是NLPlanet为您选择的有关NLP和AI的每周文章&#x…

Linux上安装温度监控软件

文章目录 Linux上安装温度监控软件IDRAC设置 Linux上安装温度监控软件 服务器的温度是影响服务器性能重要条件&#xff0c;怎么监控机器的温度呢&#xff0c;这里知道的有两种方式 通过管理界面&#xff0c;查看机器的温度通过机器上安装监监控软件来监控温度 在物理机上怎么…

SAP AIF-Application Interface Framework基本介绍

AIF-Application Interface Framework基本介绍 SAP AIF-应用程序接口框架特性&#xff1a; 通知业务用户出错的自动警报&#xff1b; 用户友好的事务&#xff0c;用于界面监控、错误处理和直接从应用系统内纠正错误&#xff1b; SAP GUI 和基于 Web 的用户界面&#xff1b; 使…

8月10日计算机考研信息差

距24考研初试还有134 天。 8月关注&#xff1a;科目调整、招生宣讲、推荐免试 下一关键节点&#xff1a;预报名(预计2023年09月24日-09月27日) 计算机考研最新资讯&#xff1a; 1、哈尔滨工业大学(威海)发布24专业目录 计算机相关专业&#xff1a; 0812计算机科学与技术 …

Go语言工程实践之测试与Gin项目实践

Go 语言并发编程 及 进阶与依赖管理_软工菜鸡的博客-CSDN博客 03 测试 回归测试一般是QA(质量保证)同学手动通过终端回归一些固定的主流程场景 集成测试是对系统功能维度做测试验证,通过服务暴露的某个接口,进行自动化测试 而单元测试开发阶段&#xff0c;开发者对单独的函数…

在单元测试中使用Jest模拟VS Code extension API

对VS Code extension进行单元测试时通常会遇到一个问题&#xff0c;代码中所使用的VS Code编辑器的功能都依赖于vscode库&#xff0c;但是我们在单元测试中并没有添加对vscode库的依赖&#xff0c;所以导致运行单元测试时出错。由于vscode库是作为第三方依赖被引入到我们的VS C…