linux系统编程重点复习--线程同步

news2024/11/15 16:49:38

目录

复习目标:

1 互斥锁

1.1互斥锁的使用步骤

1.2 练习

1.3 死锁

2 读写锁

3 条件变量

4 信号量


复习目标:

  • 熟练掌握互斥量的使用
  • 说出什么叫死锁以及解决方案
  • 熟练掌握读写锁的使用
  • 熟练掌握条件变量的使用
  • 理解条件变量实现的生产消费者模型
  • 理解信号量实现的生产消费者模型

1 互斥锁

1.1互斥的使用步骤

  • 第1步:创建一把互斥锁
  • pthread_mutex_t mutex;
  • 初始化互斥锁
  • pthread_mutex_init(&mutex);---相当于mutex=1
  • 在代码中寻找共享资源(也称为临界区)

pthread_mutex_lock(&mutex);  -- mutex = 0

[临界区代码]

pthread_mutex_unlock(&mutex); -- mutex = 1

  • 释放互斥锁资源

pthread_mutex_destroy(&mutex);

注意:必须在所有操作共享资源的线程上都加上锁否则不能起到同步的效果

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <sys/types.h>

//定义一把锁
pthread_mutex_t mutex;

void *mythread1(void *args)
{

    while (1)
    {

        //加锁
        pthread_mutex_lock(&mutex);
        printf("hello ");
        sleep(rand() % 3);

        printf("world\n");

        //解锁
        pthread_mutex_unlock(&mutex);
        sleep(rand() % 3);
    }

    pthread_exit(NULL);
}

void *mythread2(void *args)
{
    while (1)
    {

        //解锁
        pthread_mutex_lock(&mutex);
        printf("HELLO");
        sleep(rand() % 3);

        printf("WORLD\n");
        //解锁
        pthread_mutex_unlock(&mutex);
        sleep(rand() % 3);
    }
    pthread_exit(NULL);
}

int main()
{

    int ret;
    pthread_t thread1;
    pthread_t thread2;

    //随机数种子
    srand(time(NULL));
    //互斥锁初始化
    pthread_mutex_init(&mutex, NULL);

    ret = pthread_create(&thread1, NULL, mythread1, NULL);
    if (ret != 0)
    {
        printf("pthread_create error ,[%s]\n", strerror(ret));
        return -1;
    }
    ret = pthread_create(&thread2, NULL, mythread2, NULL);
    if (ret != 0)
    {
        printf("pthread_create error ,[%s]\n", strerror(ret));
        return -1;
    }
    //等待线程结束
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    //释放互斥锁
    pthread_mutex_destroy(&mutex);

    system("pause");
    return 0;
}

 

1.2 练习

  • 编写思路:

1 定义一把互斥锁,应该为一全局变量

pthread_mutex_t mutex;

2 在main函数中对mutex进行初始化

pthread_mutex_init(&mutex, NULL);

3 创建两个线程,在两个线程中加锁和解锁

4 主线程释放互斥锁资源

pthread_mutex_destroy(&mutex);

1.3 死锁

死锁并不是linux提供给用户的一种使用方法,而是由于用户使用互斥锁不当引起的一种现象。

  • 常见的死锁有两种:
  • 第一种:自己锁自己,如下图代码片段

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

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

2 读写锁

  • 什么是读写锁
  • 读写锁也叫共享-独占锁。当读写锁以读模式锁住时,它是以共享模式锁住的;当它以写模式锁住时,它是以独占模式锁住的。写独占、读共享。
  • 读写锁使用场合
  • 读写锁非常适合于对数据结构读的次数远大于写的情况。
  • 读写锁特性
  • 读写锁是“写模式加锁”时,解锁前,所有对该锁加锁的线程都会被阻塞。
  • 读写锁是“读模式加锁”时,如果线程以读模式对其加锁会成功;如果线程以写模式加锁会阻塞。
  • 读写锁是“读模式加锁”时, 既有试图以写模式加锁的线程,也有试图以读模式加锁的线程。那么读写锁会阻塞随后的读模式锁请求。优先满足写模式锁。读锁、写锁并行阻塞,写锁优先级高
  • 读写锁场景练习:
  1. 线程A加写锁成功, 线程B请求读锁
  • 线程B阻塞
  1. 线程A持有读锁, 线程B请求写锁
  • 线程B阻塞
  1. 线程A拥有读锁, 线程B请求读锁
  • 线程B加锁成功
  1. 线程A持有读锁, 然后线程B请求写锁, 然后线程C请求读锁
  • B阻塞,c阻塞 - 写的优先级高
  • A解锁,B线程加写锁成功,C继续阻塞
  • B解锁,C加读锁成功
  1. 线程A持有写锁, 然后线程B请求读锁, 然后线程C请求写锁
  • BC阻塞
  • A解锁,C加写锁成功,B继续阻塞
  • C解锁,B加读锁成功
  • 读写锁总结

读并行,写独占,当读写同时等待锁的时候写的优先级高

  • 读写锁主要操作函数
  1. 定义一把读写锁
  1. pthread_rwlock_t rwlock;
  1. 初始化读写锁
  • int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);
  • 函数参数
  1. rwlock-读写锁
  2. attr-读写锁属性,传NULL为默认属性
  • 销毁读写锁

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);        

  • 加读锁

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);              

  • 尝试加读锁

int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);

  • 加写锁

int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);

  • 尝试加

int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);

  • 解锁

int pthread_rwlock_unlock(&pthread_rwlock_t *rwlock);

  • 练习:3个线程不定时写同一全局资源,5个线程不定时读同一全局资源。
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>

//读写锁测试程序
int number = 0;

//定义一把读写锁
pthread_rwlock_t rwlock;

//读写锁的回调函数
void *thread_write(void *arg)
{

    int i = *(int *)arg;
    int cur;

    while (1)
    {
        //加读写锁
        pthread_rwlock_wrlock(&rwlock);

        cur = number;
        cur++;
        number = cur;
        printf("[%d]-W:[%d]\n", i, cur);

        //解锁
        pthread_rwlock_unlock(&rwlock);
        sleep(rand() % 3);
    }
}

//读回调函数
void *thread_read(void *arg)
{

    int i = *(int *)arg;
    int cur;

    while (1)
    {

        //加读锁
        pthread_rwlock_rdlock(&rwlock);
        cur = number;
        printf("[%d]-R:[%d]\n", i, cur);

        //解锁
        // pthread_rwlock_unlock(&rwlock);
        pthread_rwlock_unlock(&rwlock);
        sleep(rand() % 3);
    }
}

int main()
{

    int n = 8;
    int i = 0;
    int arr[8];
    pthread_t thread[8];

    //读写锁初始化
    pthread_rwlock_init(&rwlock, NULL);

    //创建3个写子线程

    for (i = 0; i < 3; i++)
    {
        arr[i] = i;
        pthread_create(&thread[i], NULL, thread_write, &arr[i]);
    }

    //创建5个读子线程
    for (i = 3; i < n; i++)
    {
        arr[i] = i;
        pthread_create(&thread[i], NULL, thread_read, &arr[i]);
    }

    //回收子线程
    int j = 0;
    for (j = 0; j < n; j++)
    {
        pthread_join(thread[j], NULL);
    }

    //释放锁
    pthread_rwlock_destroy(&rwlock);

    system("pause");
    return 0;
}

  • 3 条件变量

  • 条件本身不是锁!但它也可以造成线程阻塞。通常与互斥锁配合使用。给多线程提供一个会合的场所。
  • 使用互斥量保护共享数据;
  • 使用条件变量可以使线程阻塞, 等待某个条件的发生, 当条件满足的时候解除阻塞.
  • 条件变量的两个动作:
  • 条件不满足, 阻塞线程
  • 条件满足, 通知阻塞的线程解除阻塞, 开始工作.
  • 条件变量相关函数
  • pthread_cond_t  cond;
  • 定义一个条件变量
  • int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
  • 函数描述:初始化条件变量
  • 函数参数: 
  • 函数返回值:成功返回0, 失败返回错误号
  • int pthread_cond_destroy(pthread_cond_t *cond);
  • 函数描述: 销毁条件变量
  • 函数参数: 条件变量
  • 返回值: 成功返回0, 失败返回错误号
  • int pthread_cond_wait(pthread_cond_t *restrict cond,
  • 函数描述: 条件不满足, 引起线程阻塞并解锁;
  • 函数参数:
  • 函数返回值: 成功返回0, 失败返回错误号
  •  int pthread_cond_signal(pthread_cond_t *cond);
  • 函数描述: 唤醒至少一个阻塞在该条件变量上的线程
  • 函数参数: 条件变量
  • 函数返回值: 成功返回0, 失败返回错误号

cond: 条件变量

attr: 条件变量属性, 通常传NULL

               pthread_mutex_t *restrict mutex);

          条件满足, 解除线程阻塞, 并加锁

cond: 条件变量

mutex: 互斥锁变量

使用条件变量的代码片段

上述代码中,生产者线程调用pthread_cond_signal函数会使消费者线程在pthread_cond_wait处解除阻塞。

//使用条件变量实现生产者和消费者模型
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>

typedef struct node
{
    int data;
    struct node *next;
} NODE;

NODE *head = NULL;

//定义一把锁
pthread_mutex_t mutex;

//定义条件变量
pthread_cond_t cond;

//生产者线程
void *producer(void *arg)
{

    NODE *pNode = NULL;

    while (1)
    {

        pNode = (NODE *)malloc(sizeof(NODE));
        if (pNode == NULL)
        {
            perror("malloc error");
            exit(-1);
        }

        pNode->data = rand() % 1000;
        printf("P:[%d]\n", pNode->data);

        //加锁
        pthread_mutex_lock(&mutex);

        pNode->next = head;
        head = pNode;

        //解锁
        pthread_mutex_unlock(&mutex);

        //通知消费者线程解除阻塞
        pthread_cond_signal(&cond);
        sleep(rand() % 3);
    }
}

//消费者线程
void *consumer(void *arg)
{

    NODE *pNode = NULL;
    while (1)
    {
        //加锁
        pthread_mutex_lock(&mutex);

        if (head == NULL)
        {
            //若条件不满足,需要阻塞等待
            //若条件不满足,则阻塞等待并解锁;
            //若条件满足(被生成者线程调用pthread_cond_signal函数通知),解除阻塞并加锁
            pthread_cond_wait(&cond, &mutex);
        }

        printf("C:[%d]\n", head->data);
        pNode = head;
        head = head->next;

        //解锁
        pthread_mutex_unlock(&mutex);
        free(pNode);
        pNode = NULL;

        sleep(rand() % 3);
    }
}

int main()
{

    int ret;
    pthread_t thread1;
    pthread_t thread2;

    //初始化互斥锁
    pthread_mutex_init(&mutex, NULL);

    //条件变量初始化

    pthread_cond_init(&cond, NULL);

    //创建生产者线程
    ret = pthread_create(&thread1, NULL, producer, NULL);

    if (ret != 0)
    {
        printf("pthread_create error,[%s]\n", strerror(ret));
        return -1;
    }

    //创建消费者线程
    ret = pthread_create(&thread2, NULL, consumer, NULL);
    if (ret != 0)
    {
        printf("pthread_create error,[%s]\n", strerror(ret));
        return -1;
    }

    //等待线程结束
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    //释放互斥锁
    pthread_mutex_destroy(&mutex);

    //释放条件变量
    pthread_cond_destroy(&cond);

    system("pause");
    return 0;
}

多个生产者和消费者

//使用条件变量实现多个生产者和消费者模型
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
typedef struct node
{
	int data;
	struct node *next;
}NODE;

NODE *head = NULL;

//定义一把锁
pthread_mutex_t mutex;

//定义条件变量
pthread_cond_t cond;

//生产者线程
void *producer(void *arg)
{
	NODE *pNode = NULL;
	int n = *(int *)arg;
	while(1)
	{
		//生产一个节点
		pNode = (NODE *)malloc(sizeof(NODE));
		if(pNode==NULL)
		{
			perror("malloc error");
			exit(-1);
		}
		pNode->data = rand()%1000;
		printf("P[%d]:[%d]\n", n, pNode->data);

		//加锁
		pthread_mutex_lock(&mutex);

		pNode->next = head;
		head = pNode;

		//解锁
		pthread_mutex_unlock(&mutex);

		//通知消费者线程解除阻塞
		pthread_cond_signal(&cond);

		sleep(rand()%3);
	}
}


//消费者线程
void *consumer(void *arg)
{
	NODE *pNode = NULL;
	int n = *(int *)arg;
	while(1)
	{
		//加锁
		pthread_mutex_lock(&mutex);

		if(head==NULL)
		{
			//若条件不满足,需要阻塞等待
			//若条件不满足,则阻塞等待并解锁;
			//若条件满足(被生成者线程调用pthread_cond_signal函数通知),解除阻塞并加锁 
			pthread_cond_wait(&cond, &mutex);
		}

		if(head==NULL)
		{
			//解锁
			pthread_mutex_unlock(&mutex);	
			continue;
		}

		printf("C[%d]:[%d]\n", n, head->data);	
		pNode = head;
		head = head->next;

		//解锁
		pthread_mutex_unlock(&mutex);

		free(pNode);
		pNode = NULL;

		sleep(rand()%3);
	}
}

int main()
{
	int ret;
	int i = 0;
	pthread_t thread1[5];
	pthread_t thread2[5];

	//初始化互斥锁
	pthread_mutex_init(&mutex, NULL);

	//条件变量初始化
	pthread_cond_init(&cond, NULL);

	int arr[5];
	for(i=0; i<5; i++)
	{
		arr[i]= i;
		//创建生产者线程
		ret = pthread_create(&thread1[i], NULL, producer, &arr[i]);
		if(ret!=0)
		{
			printf("pthread_create error, [%s]\n", strerror(ret));
			return -1;
		}

		//创建消费者线程
		ret = pthread_create(&thread2[i], NULL, consumer, &arr[i]);
		if(ret!=0)
		{
			printf("pthread_create error, [%s]\n", strerror(ret));
			return -1;
		}
	}

	//等待线程结束
	for(i=0; i<5; i++)
	{
		pthread_join(thread1[i], NULL);
		pthread_join(thread2[i], NULL);
	}

	//释放互斥锁
	pthread_mutex_destroy(&mutex);

	//释放条件变量
	pthread_cond_destroy(&cond);

	return 0;
}

4 信号量

1 信号量介绍

信号量相当于多把锁, 可以理解为是加强版的互斥锁

2 相关函数

定义信号量 sem_t sem; int sem_init(sem_t *sem, int pshared, unsigned int value);

pshared: 0表示线程同步, 1表示进程同步

value: 最多有几个线程操作共享数据

sem: 信号量变量

 3 信号量代码片段:

  • 函数描述: 初始化信号量
  • 函数参数:
  • 函数返回值:成功返回0, 失败返回-1, 并设置errno值
  • int sem_wait(sem_t *sem);
  • 函数描述: 调用该函数一次, 相当于sem--, 当sem为0的时候, 引起阻塞
  • 函数参数: 信号量变量
  • 函数返回值: 成功返回0, 失败返回-1, 并设置errno值
  • int sem_post(sem_t *sem);
  • 函数描述: 调用一次, 相当于sem++
  • 函数参数: 信号量变量
  • 函数返回值: 成功返回0, 失败返回-1, 并设置errno值
  • int sem_trywait(sem_t *sem);
  • 函数描述: 尝试加锁, 若失败直接返回, 不阻塞
  • 函数参数: 信号量变量
  • 函数返回值: 成功返回0, 失败返回-1, 并设置errno值
  • int sem_destroy(sem_t *sem);
  • 函数描述: 销毁信号量
  • 函数参数: 信号量变量
  • 函数返回值: 成功返回0, 失败返回-1, 并设置errno值
//使用信号量实现生产者和消费者模型
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
//信号量的头文件
#include <semaphore.h>

typedef struct node
{
    int data;
    struct node *next;
} NODE;

NODE *head = NULL;

//定义信号量
sem_t sem_producer;
sem_t sem_consumer;

//生产者线程

void *producer(void *arg)
{

    NODE *pNode = NULL;
    while (1)
    {

        pNode = (NODE *)malloc(sizeof(NODE));

        if (pNode == NULL)
        {
            perror("malloc error");
            exit(-1);
        }

        pNode->data = rand() % 100;
        printf("P;[%d]\n", pNode->data);

        //加锁
        sem_wait(&sem_producer);

        pNode->next = head;
        head = pNode;

        //解锁
        sem_post(&sem_consumer);
        sleep(rand() % 3);
    }
}

void *consumer(void *arg)
{

    NODE *pNode = NULL;

    while (1)
    {

        //加锁
        sem_wait(&sem_consumer); //相当于--

        printf("C:[%d]\n", head->data);
        pNode = head;
        head = head->next;

        //解锁
        sem_post(&sem_producer); //相当于++

        free(pNode);
        pNode = NULL;

        sleep(rand() % 3);
    }
}

int main()
{

    int ret;
    pthread_t thread1;
    pthread_t thread2;

    //初始化信号量
    sem_init(&sem_producer, 0, 5);
    sem_init(&sem_consumer, 0, 0);

    //创建生产者线程
    ret = pthread_create(&thread1, NULL, producer, NULL);

    if (ret != 0)
    {
        printf("pthread create error,[%s]\n", strerror(ret));
        return -1;
    }
    ret = pthread_create(&thread2, NULL, consumer, NULL);
    if (ret != 0)
    {
        printf("pthread create error,[%s]\n", strerror(ret));
        return -1;
    }

    //等待线程结束
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    //释放信号量资源
    sem_destroy(&sem_producer);
    sem_destroy(&sem_consumer);

    system("pause");
    return 0;
}

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

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

相关文章

Java基础篇_1.2——保留关键字、基本数据类型、基本数据类型之间的转换

​​​​​​​目录 一、保留关键字 二、Java的基本数据类型 三、引用数据类型 四、基本数据类型间的转换 隐含强制类型转换 一、保留关键字 Java该语言是用 Unicode 字符集编写的。 Java关键字是预先定义的具有特别意义的标识符&#xff0c;也被称为Java保留字&#xff0…

在k8s集群内搭建Prometheus监控平台

基本架构 Prometheus由SoundCloud发布&#xff0c;是一套由go语言开发的开源的监控&报警&时间序列数据库的组合。 Prometheus的基本原理是通过HTTP协议周期性抓取被监控组件的状态&#xff0c;任意组件只要提供对应的HTTP接口就可以接入监控。不需要任何SDK或者其他的…

YOLOv8教程系列:三、使用YOLOv8模型进行自定义数据集半自动标注

YOLOv8半自动标注 目标检测半自动标注的优点包括&#xff1a; 1.提高标注效率&#xff1a;算法能够自动标注部分数据&#xff0c;减少了人工标注的工作量&#xff0c;节省时间和资源。 2.降低成本&#xff1a;自动标注可以减少人工标注的成本&#xff0c;特别是对于大规模数据…

如何在线制作闪图?手把手教你快速生成GIF闪图

网上那种卟玲卟领的闪动GIF图片效果非常的炫丽&#xff0c;这种GIF闪图是怎么制作的呢&#xff1f;很简单&#xff0c;只需要使用专业的gif制作&#xff08;https://www.gif.cn/&#xff09;工具-GIF中文网&#xff0c;上传多张颜色、大小不同&#xff0c;内容相同的jpg、png格…

ORB-SLAM3数据集配置与评价

在ORB-SLAM3运行EuRoC和TUM-VI数据集并作以评价。EuRoC利用微型飞行器(MAV ) 收集的视觉惯性数据集&#xff0c;TUM-VI 是由实验人员手持视觉-惯性传感器收集的数据集。这两个是在视觉SLAM中比较常用的公开数据集&#xff0c;所以测试并加以记录。 文章目录 一、EuRoC数据集测…

音频转文字软件免费版让你快速完成转换

音频转文字技术是一种将音频文件转换为文本形式的技术&#xff0c;它可以帮助人们更方便地获取和处理音频信息。在实际生活和工作中&#xff0c;我们可能会遇到需要将音频转换为文字的情况&#xff0c;比如听取会议录音、收听讲座、学习外语等等。那么&#xff0c;你知道音频转…

Tinkercad 建模21个小技巧

21个Tinkercad 建模小技巧 原文 参考文章&#xff1a;在 Tinkercad 中加快设计的 22 个技巧 一起来了解一下21个Tinkercad 3D建模小技巧&#xff0c;让你快人一步。 技巧1 Copy & Paste 文件&#xff0c;整合设计 想把文件A里面的模型拷贝到文件B里面&#xff1f; 很容…

【Linux命令200例】mren一个用于重命名文件或目录的命令行工具

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;全栈领域新星创作者✌&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;本文已收录于专栏&#xff1a;Linux命令大全。 &#x1f3c6;本专栏我们会通过具体的系统的命令讲解加上鲜活的实操案例对各个命令进行深入…

msvcr100.dll丢失怎样修复?最新的三个修复方法可解决

最近我遇到了一个问题&#xff0c;电脑提示我缺少了msvcr100.dll文件&#xff0c;导致某些程序无法正常运行。这让我意识到了计算机中的一些系统文件的重要性&#xff0c;也让我体会到了修复这类问题的必要性。msvcr100.dll文件是Windows系统中重要的文件&#xff0c;这是一个动…

docker容器的安装(windows、linux本地安装和在线安装)

目录 一、Docker发行版本&#xff1a; 1、Windows安装Docker&#xff08;作为了解&#xff09; 2、Linux安装Docker 二、安装前准备&#xff1a; 三、默认的yum安装 四、安装docker-ce 五、阿里云镜像加速器 Docker支持在主流的操作系统平台上使用&#xff0c;包括Windo…

飞致云开源社区月度动态报告(2023年7月)

自2023年6月起&#xff0c;中国领先的开源软件公司FIT2CLOUD飞致云将以月度为单位发布《飞致云开源社区月度动态报告》&#xff0c;旨在向广大社区用户同步飞致云旗下系列开源软件的发展情况&#xff0c;以及当月主要的产品新版本发布、社区运营成果等相关信息。 飞致云开源大…

el-table点击表格某一行添加到URL参数,访问带参URL加载表格内容并滚动到选中行位置 [Vue3] [Element-plus 2.3]

写在最前 需求&#xff1a;有个表格列出了一些行数据&#xff0c;每个行数据点击后会加载出对应的详细数据&#xff0c;想要在点击了某一行后&#xff0c;能够将该点击反应到URL中&#xff0c;这样我复制这个URL发给其他人&#xff0c;他们打开时也能看到同样的行数据。 url会根…

app自动化测试之Appium问题分析及定位

使用 Appium 进行测试时&#xff0c;会产生大量日志&#xff0c;一旦运行过程中遇到报错&#xff0c;可以通过 Appium 服务端的日志以及客户端的日志分析排查问题。 Appium Server日志-开启服务 通过命令行的方式启动 Appium Server&#xff0c;下面来分析一下启动日志&#…

C# 模拟 Unity3d 协程

一、概述 由于 Unity3d 在开发游戏时使用的是单线程&#xff0c;为了给开发者提供异步相关的操作&#xff0c;于是开发者在 Unity3d 中加入了协程的概念&#xff0c;协程在 Unity3d 中用的非常多&#xff0c;也有些大佬觉得这玩意儿不好用&#xff0c;还不如用一些插件。 在 …

若依vue -【 44 ~ 53 】

44 服务监控讲解 1 需求 显示CPU、内存、服务器信息、Java虚拟机信息、磁盘状态的信息 2 前端 RuoYi-Vue\ruoyi-ui\src\views\monitor\server\index.vue <script> import { getServer } from "/api/monitor/server";export default {name: "Server&quo…

Linux 查看磁盘空间

1 查看当前目录的总大小 &#xff1a;du -sh ps&#xff1a;du(disk usage) 2 查看某个目录的总大小&#xff1a;du -sh 目录名 3 查找出/目录下占用空间最大的前10个文件或者文件夹&#xff1a;sudo du -a / | sort -n -r | head -n 10 4 查看磁盘信息:df -h

旧项目导入Eclipse时文件夹看起来乱七八糟,无从下手的解决办法(无main或webapp等文件夹)

首先&#xff0c;如果没有main或java/resource/webapp等文件夹&#xff0c;那就自己在src下面创建一个&#xff0c;只要对应关系与我下图左边红框一致即可&#xff0c;创建完之后java文件移到java文件夹下&#xff0c;资源文件例如.properties、老项目的数据源定义.INI文件、日…

Vue2 第十二节 Vue组件化编程(一)

1.模块与组件&#xff0c;模块化与组件化概念 2. 非单文件组件 3. 组件编写注意事项 4. 组件的嵌套 一. 模块与组件&#xff0c;模块化与组件化 传统方式编写存在的问题 &#xff08;1&#xff09;依赖关系混乱&#xff0c;不好维护 &#xff08;2&#xff09;代码的复用…

基于SWAT-MODFLOW地表水与地下水耦合

目录 第一讲 模型原理剖析 第二讲 QGIS高级操作 第三讲 QSWATMOD操作 第四讲 数据制备 第五讲 基于CUP的率定验证 第六讲 结果分析 第七讲 控制措施效果模拟 第八讲 土地变化情景模拟 第九讲 气候变化情景模拟 更多推荐 耦合模型被应用到很多科学和工程领域来改善模型…

谷歌: 安卓补丁漏洞让 N-days 与 0-days 同样危险

近日&#xff0c;谷歌发布了年度零日漏洞报告&#xff0c;展示了 2022 年的野外漏洞统计数据&#xff0c;并强调了 Android 平台中长期存在的问题&#xff0c;该问题在很长一段时间内提高了已披露漏洞的价值和使用。 更具体地说&#xff0c;谷歌的报告强调了安卓系统中的 &quo…