操作系统 线程同步实验

2023-11-09

操作系统 线程同步实验

一、实验目标:

  1. 顺序表循环队列实现的实验目标:

掌握使用顺序表和循环队列实现队列的基本操作,如队列的插入、删除、遍历等,同时了解循环队列的内部实现原理和利用循环队列解决实际问题的方法。

  1. Linux生产者/消费者问题的多线程示例实验目标:

熟悉Linux下多线程编程的开发环境和基本思路,学会使用线程同步机制(如互斥锁、条件变量等)解决多线程编程中的共享资源竞争问题,实现一个生产者/消费者模型的多线程示例程序。同时,掌握Linux下多线程编程的基本调试技巧和方法。

二、实验内容:

本次实验主要包括两部分,一是顺序表循环队列的实现,二是Linux生产者/消费者问题的多线程示例。在顺序表循环队列的实现中,将通过数组实现一个循环队列,支持入队和出队操作,并对队列进行相关的操作和处理。在Linux生产者/消费者问题的多线程示例中,将使用多线程技术对商品进行生产和消费,同时通过使用锁机制实现对生产和消费过程的同步和协调。

三、实验步骤:

1、顺序循环队列的实现

队列的结构体变量定义和相关操作函数的声明 在顺序表循环队列的实现中,首先定义了队列结构体变量,并声明了相关操作函数,包括队列的初始化、入队、出队、遍历等操作,具体代码如下:

首先定义循环队列结构如下:

#define QUEUE_SIZE   5 //队列最大容纳QUEUE_SIZE-1个元素
typedef struct{
  int aData[QUEUE_SIZE];  //队列元素
  int dwHead;  //指向队首元素
  int dwTail;  //指向队尾元素的下一个元素
}T_QUEUE, *PT_QUEUE;

提供判空、判满、查询等辅助函数:

//判断循环队列是否为空
int IsQueEmpty(PT_QUEUE ptQue)
{
  return ptQue->dwHead == ptQue->dwTail;
}


//判断循环队列是否为满
int IsQueFull(PT_QUEUE ptQue)
{
  return (ptQue->dwTail + 1) % QUEUE_SIZE == ptQue->dwHead;
}


//获取循环队列元素数目
int QueDataNum(PT_QUEUE ptQue)
{
  return (ptQue->dwTail - ptQue->dwHead + QUEUE_SIZE) % QUEUE_SIZE;
}


//获取循环队列队首位置
int GetQueHead(PT_QUEUE ptQue)
{
  return ptQue->dwHead;
}


//获取循环队列队首元素
int GetQueHeadData(PT_QUEUE ptQue)
{
  return ptQue->aData[ptQue->dwHead];
}


//获取循环队列队尾位置
int GetQueTail(PT_QUEUE ptQue)
{
  return ptQue->dwTail;
}

基于上述结构,定义初始化、入队出队和显示函数:

//初始化循环队列
void InitQue(PT_QUEUE ptQue)
{
  memset(ptQue, 0, sizeof(*ptQue));
}


//向循环队列中插入元素
void EnterQue(PT_QUEUE ptQue, int dwElem)
{
  if(IsQueFull(ptQue))
  {
    printf("Elem %d cannot enter Queue %p(Full)!\n", dwElem, ptQue);
    return;
  }
  ptQue->aData[ptQue->dwTail]= dwElem;
  ptQue->dwTail = (ptQue->dwTail + 1) % QUEUE_SIZE;
}


//从循环队列中取出元素
int LeaveQue(PT_QUEUE ptQue)
{
  if(IsQueEmpty(ptQue))
  {
    printf("Queue %p is Empty!\n", ptQue);
    return -1;
  }
  int dwElem = ptQue->aData[ptQue->dwHead];
  ptQue->dwHead = (ptQue->dwHead + 1) % QUEUE_SIZE;
  return dwElem;
}


//从队首至队尾依次显示队中元素值
void DisplayQue(PT_QUEUE ptQue)
{
  if(IsQueEmpty(ptQue))
  {
    printf("Queue %p is Empty!\n", ptQue);
    return;
  }
}
printf("Queue Element: ");
int dwIdx = ptQue->dwHead;
while((dwIdx % QUEUE_SIZE) != ptQue->dwTail)
    printf("%d ", ptQue->aData[(dwIdx++) % QUEUE_SIZE]);


printf("\n");

测试代码中展示了循环队列的基本操作,包括队列的初始化、入队、出队以及队列元素的遍历输出。

生产者消费者问题多线程实现

#define PRODUCER_NUM   5  //生产者数
#define CONSUMER_NUM   3  //消费者数
#define PRD_NUM        20 //最多生产的产品数
#define DELAY_TIME     3  //生产(或消费)任务之间的最大时间间隔
#define QUEUE_SIZE     (PRD_NUM+1) //队列最大容纳QUEUE_SIZE-1个元素


T_QUEUE gtQueue;
pthread_mutex_t gtQueLock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t gtPrdCond = PTHREAD_COND_INITIALIZER; //Full->Not Full
pthread_cond_t gtCsmCond = PTHREAD_COND_INITIALIZER; //Empty->Not Empty

在多线程实现生产者消费者问题时,需要定义线程共享的全局数据,包括生产者消费者线程数目,产品数目,队列大小,互斥锁和条件变量等。

void *ProducerThread(void *pvArg)
{
    pthread_detach(pthread_self());
    intptr_t dwThrdNo = (intptr_t)pvArg;


    while(1)
    {
        pthread_mutex_lock(&gtQueLock);
        while(IsQueFull(&gtQueue))  //队列由满变为非满时,生产者线程被唤醒
            pthread_cond_wait(&gtPrdCond, &gtQueLock);


        EnterQue(&gtQueue, GetQueTail(&gtQueue)); //将队列元素下标作为元素值入队
        if(QueDataNum(&gtQueue) == 1) //当生产者开始产出后,通知(唤醒)消费者线程
            pthread_cond_broadcast(&gtCsmCond);
        printf("[Producer %2ld]Current Product Num: %u\n", dwThrdNo, QueDataNum(&gtQueue));


        pthread_mutex_unlock(&gtQueLock);
        sleep(rand()%DELAY_TIME + 1);
    }
}


void *ConsumerThread(void *pvArg)
{
    pthread_detach(pthread_self());
    intptr_t dwThrdNo = (intptr_t)pvArg;


    while(1)
    {
        pthread_mutex_lock(&gtQueLock);
        while(IsQueEmpty(&gtQueue)) //队列由空变为非空时,消费者线程将被唤醒
            pthread_cond_wait(&gtCsmCond, &gtQueLock);


        if(GetQueHead(&gtQueue) != GetQueHeadData(&gtQueue))
        {
            printf("[Consumer %2ld]Product: %d, Expect: %d\n", dwThrdNo,
                   GetQueHead(&gtQueue), GetQueHeadData(&gtQueue));
            exit(0);
        }
        LeaveQue(&gtQueue);
        if(QueDataNum(&gtQueue) == (PRD_NUM-1)) //当队列由满变为非满时,通知(唤醒)生产者线程
            pthread_cond_broadcast(&gtPrdCond);
        printf("[Consumer %2ld]Current Product Num: %u\n", dwThrdNo, QueDataNum(&gtQueue));


        pthread_mutex_unlock(&gtQueLock);
        sleep(rand()%DELAY_TIME + 1);
    }
}


int main(void)
{
    InitQue(&gtQueue);
    srand(getpid());  //初始化随机函数发生器


    pthread_t aThrd[CONSUMER_NUM+PRODUCER_NUM];
    int dwThrdIdx;
    for(dwThrdIdx = 0; dwThrdIdx < CONSUMER_NUM; dwThrdIdx++)
    {  //创建CONSUMER_NUM个消费者线程,传入(void*)dwThrdIdx作为ConsumerThread的参数
        pthread_create(&aThrd[dwThrdIdx], NULL, ConsumerThread, (void*)(intptr_t)dwThrdIdx);
    }
    sleep(2);
    for(dwThrdIdx = 0; dwThrdIdx < PRODUCER_NUM; dwThrdIdx++)
    {
        pthread_create(&aThrd[dwThrdIdx+CONSUMER_NUM], NULL, ProducerThread, (void*)(intptr_t)dwThrdIdx);
    }
    while(1);
    return 0 ;
}

在主函数中,先初始化互斥锁和条件变量,然后创建消费者线程和生产者线程,最后进行阻塞等待。在生产者线程中,使用条件变量实现队列非满时才进行入队操作,并使用生产者唤醒消费者的方式通知队列非空;在消费者线程中,使用条件变量实现队列非空时才进行出队操作,并使用消费者唤醒生产者的方式通知队列非满。这样就能够实现基本的生产者消费者同步。

四、实验结果:

1、循环队列测试结果

在这里插入图片描述

2、生产者-消费者示例运行结果

在这里插入图片描述

五、源代码

1、循环队列

#include<stdio.h> 
#include<stdlib.h>
#include<string.h> 
#define QUEUE_SIZE 21//
typedef struct{
    /* data */
    int aData[QUEUE_SIZE];
    int dwHead;
    int dwTail;
}T_QUEUE, *PT_QUEUE;

int IsQueFull(PT_QUEUE ptQue) {
    return (ptQue->dwTail+1) %QUEUE_SIZE == ptQue->dwHead;
}

int IsQueEmpty(PT_QUEUE ptQue) {
    return ptQue->dwHead == ptQue->dwTail;
}

int QueDataNum(PT_QUEUE ptQue) {
    return (ptQue->dwHead - ptQue->dwTail + QUEUE_SIZE) % QUEUE_SIZE;
}

int GetQueHead(PT_QUEUE ptQue) {
    return ptQue->dwHead;
}

int GetQueHeadData(PT_QUEUE ptQue) {
    return ptQue->aData[ptQue->dwHead];
}

int GetQueTail(PT_QUEUE ptQue) {
    return ptQue->dwTail;
}

void InitQue(PT_QUEUE ptQue) {
    memset(ptQue,0, sizeof(*ptQue));
}

void EnterQue(PT_QUEUE ptQue, int dwElem)
{
    if(IsQueFull(ptQue))
    {
        printf("Elem %d cannot enter Queue %p(Full)!\n", dwElem, ptQue);
        return;
    }
    ptQue->aData[ptQue->dwTail]= dwElem;
    ptQue->dwTail = (ptQue->dwTail + 1) % QUEUE_SIZE;
}

int LeaveQue(PT_QUEUE ptQue) {
    if (IsQueEmpty(ptQue))
    {
        /* code */
        printf("Queue %p is Empty!\n", ptQue);
        return -1;
    }
    int dwElem = ptQue->aData[ptQue->dwHead];
    ptQue->dwHead = (ptQue->dwHead+1)%QUEUE_SIZE;
    return dwElem;
}

void DisplayQue(PT_QUEUE ptQue) {
    if (IsQueEmpty(ptQue))
    {
        /* code */
        printf("Queue %p is Empty!\n", ptQue);
        return ;
    }
    printf("Queue Element: ");
    int dwIdx = ptQue->dwHead;
    while((dwIdx % QUEUE_SIZE) != ptQue->dwTail)
        printf("%d", ptQue->aData[(dwIdx++) % QUEUE_SIZE]);
    
    printf("\n");
}
// 测试函数
static T_QUEUE gtQueue;
void QueueTest(void)
{
    InitQue(&gtQueue);
    printf("Enter Queue 1,2,3,4,5...\n");
    EnterQue(&gtQueue, 1);
    EnterQue(&gtQueue, 2);
    EnterQue(&gtQueue, 3);
    EnterQue(&gtQueue, 4);
    EnterQue(&gtQueue, 5);
    printf("Queue Status: Empty(%d), Full(%d)\n", IsQueEmpty(&gtQueue), IsQueFull(&gtQueue));
    printf("Queue Elem Num: %u\n", QueDataNum(&gtQueue));
    printf("Head: %u(%d)\n", GetQueHead(&gtQueue), GetQueHeadData(&gtQueue));
    printf("Tail: %u\n", GetQueTail(&gtQueue));
    DisplayQue(&gtQueue);

    printf("\nLeave Queue...\n");
    printf("Leave %d\n", LeaveQue(&gtQueue));
    printf("Leave %d\n", LeaveQue(&gtQueue));
    printf("Leave %d\n", LeaveQue(&gtQueue));
    DisplayQue(&gtQueue);

    printf("\nEnter Queue 6,7...\n");
    EnterQue(&gtQueue, 6);
    EnterQue(&gtQueue, 7);
    DisplayQue(&gtQueue);

    printf("\nLeave Queue...\n");
    printf("Leave %d\n", LeaveQue(&gtQueue));
    printf("Leave %d\n", LeaveQue(&gtQueue));
    printf("Leave %d\n", LeaveQue(&gtQueue));
    DisplayQue(&gtQueue);
}
// 在当前函数测试时打开
// int main() {
//     QueueTest();
//     return -1;
// }

2、生产消费者

#include<stdio.h> 
#include<stdlib.h> 
#include<unistd.h> 
#include <stdint.h>//用于定义intptr_t类型
#include<pthread.h>//线程相关操作的头文件 
#include<semaphore.h> //信号量相关操作的头文件
#include "T_QUEUE.c"//自定义队列的头文件

#define PRODUCER_NUM 5 //生产者线程数量
#define CONSUMER_NUM 3 //消费者线程数量
#define PRD_NUM  20//队列最大容量
#define DELAY_TIME 3 //任务生产或消费的最长延迟时间

static T_QUEUE gtQueue;//定义一个队列的全局变量
pthread_mutex_t gtQueLock = PTHREAD_MUTEX_INITIALIZER;//定义静态互斥锁
pthread_cond_t gtPrdCond = PTHREAD_COND_INITIALIZER; //定义生产者条件变量,当队列未满时通知生产者继续生产,只在添加元素时使用
pthread_cond_t gtCsmCond = PTHREAD_COND_INITIALIZER; //定义消费者条件变量,当队列不为空时通知消费者继续消费,只在删除元素时使用

void *ProducerThread(void *pvArg)//生产者线程函数
{
    pthread_detach(pthread_self());//以分离状态创建线程,释放线程运行后的资源
    intptr_t dwThrdNo = (intptr_t)pvArg;//强制类型转换,获取线程编号,并将pvArg转化为intptr_t类型        
    while(1)//无限生产
    {
        pthread_mutex_lock(&gtQueLock);//上锁,保证线程操作间的互斥性
        while(IsQueFull(&gtQueue))//如果队列满了,等待条件变量
            pthread_cond_wait(&gtPrdCond, &gtQueLock);//等待是否队列非满状态

        EnterQue(&gtQueue, GetQueTail(&gtQueue)); //把新产品放到队列尾部
        if(QueDataNum(&gtQueue) == 1)//如果现在队列中就只有一个产品
            pthread_cond_broadcast(&gtCsmCond);//广播消费者,通知其可以消费了
        printf("[Producer %2ld]Current Product Num: %u\n", dwThrdNo, QueDataNum(&gtQueue));//打印当前队列中产品的数量

        pthread_mutex_unlock(&gtQueLock);//解锁,方便其他线程进行操作
        sleep(rand()%DELAY_TIME + 1);//模拟生产时间
    }
}

void *ConsumerThread(void *pvArg)//消费者线程函数
{
    pthread_detach(pthread_self());//以分离状态创建线程,释放线程运行后的资源
    intptr_t dwThrdNo = (intptr_t)pvArg;//强制类型转换,获取线程编号,并将pvArg转化为intptr_t类型
    while(1)//无限消费
    {
        pthread_mutex_lock(&gtQueLock);//上锁,保证线程操作间的互斥性
        while(IsQueEmpty(&gtQueue))//如果队列为空,等待条件变量
            pthread_cond_wait(&gtCsmCond, &gtQueLock);//等待是否队列非空状态

        if(GetQueHead(&gtQueue) != GetQueHeadData(&gtQueue))//取队列头部产品并且和期望产品编号比较
        {
            printf("[Consumer %2ld]Product: %d, Expect: %d\n", dwThrdNo,
                   GetQueHead(&gtQueue), GetQueHeadData(&gtQueue));
            exit(0);//如果不匹配则退出程序
        }
        LeaveQue(&gtQueue);//取出队列头部
        if(QueDataNum(&gtQueue) == (PRD_NUM-1))//如果现在队列中就只有一个产品
            pthread_cond_broadcast(&gtPrdCond);//广播生产者,通知其可以生产了
        printf("[Consumer %2ld]Current Product Num: %u\n", dwThrdNo, QueDataNum(&gtQueue));//打印当前队列中产品的数量

        pthread_mutex_unlock(&gtQueLock);//解锁,方便其他线程进行操作
        sleep(rand()%DELAY_TIME + 1);//模拟消费时间
    }
}

int main(void)
{
    InitQue(&gtQueue);//初始化队列
    srand(getpid());  //以当前进程ID作为随机数生成种子

    pthread_t aThrd[CONSUMER_NUM+PRODUCER_NUM];//线程号数组
    int dwThrdIdx;
    for(dwThrdIdx = 0; dwThrdIdx < CONSUMER_NUM; dwThrdIdx++)//创建消费者线程
    {  
        pthread_create(&aThrd[dwThrdIdx], NULL, ConsumerThread, (void*)(intptr_t)dwThrdIdx);
    }
    sleep(2);//留2秒的时间给消费者线程准备
    for(dwThrdIdx = 0; dwThrdIdx < PRODUCER_NUM; dwThrdIdx++)//创建生产者线程
    {
        pthread_create(&aThrd[dwThrdIdx+CONSUMER_NUM], NULL, ProducerThread, (void*)(intptr_t)dwThrdIdx);
    }
    while(1);//永远运行主线程
    return 0 ;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

操作系统 线程同步实验 的相关文章

  • 无法在 Perl 中找到 DBI.pm 模块

    我使用的是 CentOS 并且已经安装了 Perl 5 20 并且默认情况下存在 Perl 5 10 我正在使用 Perl 5 20 版本来执行 Perl 代码 我尝试使用 DBI 模块并收到此错误 root localhost perl
  • Mcrt1.o和Scrt1.o有什么用?

    我坚持使用以下两个文件 即 Mcrt1 o 和 Scrt1 o 谁能帮我知道这两个文件的用途 如何使用它 我们以 gcrt1 o 为例 在使用 pg 选项编译进行性能测试时非常有用 谢谢 表格的文件 crt o总是 C 运行时启动代码 大部
  • 当用户按下打印时运行脚本,并且在脚本结束之前不开始假脱机(linux,cups)

    我需要做的是结合用户按下打印来执行 python 程序 脚本 并且在该程序退出之前不要让打印作业假脱机 原因是打印驱动程序不是开源的 我需要更改用户设置 在本例中是部门 ID 和密码 通常是每个用户 但因为这是一个信息亭 具有相同帐户的不同
  • 限制 Imagemagick 使用的空间和内存

    我在 Rails 应用程序上使用 Imagemagick 使用 rmagick 但我的服务器 Ubuntu 不是很大 当我启动转换进程时 Imagemagick 占据了我的服务器 30GB HDD 的所有位置 内存 我想限制内存和 tmp
  • 如何从 PROC 获取有关子进程的信息

    我正在尝试编写一个以几个进程作为参数的程序 然后父进程执行每个子进程并打印出一些相关的统计信息 示例 generate ls l 将生成一个程序 打印出有关 ls l 的一些统计信息 特别是其系统时间 用户时间和上下文切换次数 我不想使用
  • 如何从 C 程序中获取 NIC 详细信息?

    我想要获取连接到我的计算机的所有 NIC 的以下详细信息 1 接口名称 例如eth0 2 接口编号 如Windows http answers yahoo com question index qid 20080517041705AAOmJ
  • linux命令中括号的用途是什么[重复]

    这个问题在这里已经有答案了 我在 Linux 终端中运行以下命令 谁能告诉我 Linux 终端中括号和以下命令的用途是什么 echo GET HTTP 1 0 echo 主机 www google com echo 数控 www googl
  • 在 Ubuntu 中找不到 X11/Xlib.h

    我试图在 Linux 上使用 open gl 编写一个相当简单的程序 但在编译时它说 编译拇指 egl 我对 GL 完全陌生 不知道出了什么问题 快速搜索使用 apt search Xlib h 打开 libx11 dev 包 但纯 Ope
  • 为什么我的 Dockerfile CMD 不起作用?

    所以在我的 Dockerfile 的末尾我有这样的内容 WORKDIR home CMD django admin startproject whattt CMD bin bash 当我创建映像然后运行容器时 一切都按预期运行 没有错误 D
  • 使用 posix_spawn 启动进程

    我正在使用以下代码在 Linux 中启动新进程 pid t processID char argV 192 168 1 40 char 0 int status 1 status posix spawn processID home use
  • 如何将后台作业的输出分配给 bash 变量?

    我想在 bash 中运行后台作业并将其结果分配给一个变量 我不喜欢使用临时文件 并且希望同时运行多个类似的后台任务 root root var echo hello world root root echo var hello world
  • dlopen 或 dlclose 未调用信号处理程序

    我在随机时间内收到分段错误 我注册了信号 但发生分段错误时未调用信号处理程序 include
  • Fortran 中的共享库,最小示例不起作用

    我试图了解如何在 Linux 下的 Fortran 中动态创建和链接共享库 我有两个文件 第一个 liblol f90 看起来像这样 subroutine func print lol end subroutine func 我用它编译gf
  • 查看 Linux 上的多核或多 CPU 利用率

    我有一个在 Linux 上运行的程序 我需要确定它如何利用所有 CPU 内核 有没有什么程序可以查看这些信息 跑过 top 命令并按下 1 查看各个核心
  • XAMPP Windows 上的 Php Cron 作业

    嗯 我是这个词的新手CRON 据我所知 这是一个Unix安排特定操作在定义的时间间隔后执行的概念 我需要运行一个php文件 每小时更新一次数据库 但我的困惑在于安排执行 我在用XAMPP用于 Windows 7 上的本地开发测试 我发现了什
  • ubuntu 的 CSS 更少(并且自动编译)? [关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我尝试过 simples 但现在 l
  • Python将文件从Linux复制到WIndows

    我正在构建一个网站 该网站有一个表单 可以捕获用户数据并在用户数据上运行一些cgi cgi 的第一步是需要将文件从 Linux Web 服务器复制到 Windows 计算机 服务器将使用 Active Directory 角色帐户作为复制凭
  • 如何在文件夹中的 xml 文件中 grep 一个单词

    我知道我可以使用 grep 在这样的文件夹中的所有文件中查找单词 grep rn core 但我当前的目录有很多子目录 我只想搜索当前目录及其所有子目录中存在的所有 xml 文件 我怎样才能做到这一点 我试过这个 grep rn core
  • Bash - 比较 2 个文件列表及其 md5 校验和

    我有 2 个列表 其中包含带有 md5sum 检查的文件 即使文件相同 列表也具有不同的路径 我想检查每个文件的 md5 和 我们正在讨论数千个文件 这就是为什么我需要脚本来仅显示差异 第一个列表是普通列表 第二个列表是文件的当前状态 我想
  • 运行 shell 命令并将输出发送到文件?

    我需要能够通过 php 脚本修改我的 openvpn 身份验证文件 我已将我的 http 用户设置为免通 sudoer 因为这台机器仅在我的家庭网络中可用 我目前有以下命令 echo shell exec sudo echo usernam

随机推荐

  • 蓝桥杯2019年第十届真题-人物相关性分析

    题目 题目链接 题解 字符串 滑动区间 不想写题解了 bug没de出来 吃饭去了 代码 我的代码 不知道为什么一直就是91 有大佬帮忙看一下吗 include
  • mybatis 传递参数的7种方法

    文章目录 1 第一种方式 匿名参数 顺序传递参数 2 第二种方式 使用 Param注解 3 使用Map传递参数 4 用过java bean传递多个参数 5 直接使用JSON传递参数 6 传递集合类型参数List Set Array 7 参数
  • 以人为本

    软件团队想要保证高质量的软件交付 一般情况下会想到以下几点 多的测试人员 高薪资 福利 各种质量管理工具和手法 etc 我们有大量的实际经验表明 这些方法往往没有达到预期值 更有甚者 会不那么有效 为何会如此 通过不断的事后回顾 我想导致这
  • Kubernetes核心概念—工作负载

    Kubernetes的工作负载包括五种类型 Deployments 一个 Deployment 控制器为 Pods 和 ReplicaSets 提供声明式的更新能 StatefulSets StatefulSet 是用来管理有状态应用的工作
  • 行为型模式-解释器模式

    package per mjn pattern interpreter import java util HashMap import java util Map 环境角色类 public class Context 定义一个map集合 用
  • C++ 多态

    多态按字面的意思就是多种形态 当类之间存在层次结构 并且类之间是通过继承关联时 就会用到多态 C 多态意味着调用成员函数时 会根据调用函数的对象的类型来执行不同的函数 下面的实例中 基类 Shape 被派生为两个类 如下所示 实例 incl
  • 入行软件测试7年,才知道原来字节跳动这么容易进

    当前就业环境 裁员 失业消息满天飞 好像有一份工作就不错了 更别说高薪了 其实这只是一方面 而另一方面 各大企业依然求贤若渴 高技术人才依然紧缺 只要你技术过硬 拿个年薪50w不是问题 我的人生格言 比你优秀的人不可怕 可怕的是比你优秀的人
  • C# Winform基本知识、文件结构、控件简介

    1 程序入口 Program类 STAThread com线程模型 单线程单位 如果没有它 无法工作 Application 提供了一系列静态化方法和属性 来管理应用程序 Application EnableVisualStyles 启用应
  • Python 异常捕获与处理

    视频版教程 Python3零基础7天入门实战视频教程 异常捕获与处理 如果出现程序异常 我们不去捕获和处理 那么程序遇到异常 就直接终止 后续的代码无法继续执行 这将是可怕的事情 Python提供了完善的异常处理机制 可以实现捕获异常 处理
  • html头部代码

    学习html是件比较容易的事情 但单单学html语言肯定是不够用的 所以大多数人并没有拿html作为学习核心 而是将html作为javascript 动态语言或者css学习的必经之路 于是很多人并不关注一些其他的html标签 主流书籍大多对
  • 【JavaSE系列】第八话 —— 继承和它的边角料们

    导航小助手 思维导图 一 引出继承 二 继承的概念 三 继承的语法 四 父类成员访问 4 1 子类中访问父类的成员变量 4 2 子类访问父类的成员方法 五 super 关键字 5 1 super 成员变量 5 2 super 成员方法 5
  • QT学习 -- 12信号连接信号

    视频学习链接 https www bilibili com video BV1g4411H78N p 12 信号可以连接事件 普通函数 可以连接槽函数 也可以连接信号 一 信号触发 连接 信号 举例如下 当用鼠标点击按键 按键发出点击 cl
  • 【Leetcode】560. 和为K的子数组

    题目描述 给你一个整数数组 nums 和一个整数 k 请你统计并返回该数组中和为 k 的连续子数组的个数 题解 暴力解法 双循环 i指针从左往右走 j指针从i往左走 一个个遍历一个个加起来 直到加到等于k 就计数一次 执行用时 1445 m
  • okhttp异常: java.io.IOException: closed okio.RealBufferedSource$1.read

    java io IOException closed at okio RealBufferedSource 1 read RealBufferedSource java 405 at sun nio cs StreamDecoder rea
  • Redis——redis配置与优化

    文章目录 一 关系数据库与非关系型数据库 1 关系型数据库 2 非关系型数据库 二 Redis 简介 1 Redis的应用场景 2 Redis的优点 三 Redis 安装部署 1 安装Redis 2 配置参数 四 Redis
  • 任务管理器详解

    进程 看是否有除系统外多余进程 可能是病毒或没有完全关闭的进程 影响机器性能 进程下显示了所有当前正在运行的进程 包括应用程序 后台服务等 性能下可以看到CPU和内存 页面文件的使用情况 卡机 死机 中毒时 CPU使用率会达到100 CPU
  • mysql 用sqlyog连接1045错误解决办法(数据库在linux)

    1045 多半就是要么你端口号3306没开 要么就是你密码错误 安装网路分析 yum install net tools 防火墙开放3306端口 root localhost firewall cmd zone public add por
  • 学生信息管理系统(登录功能)

    工具eclipse 主要操作登陆 增删查改 编写实体类 public class Student private int id private String sId 学号 private String name private String
  • CTF BugKu平台———(Web篇②)

    源代码 unescape编码 https tool chinaz com Tools Escape aspx PS p1 35 34 61 61 32 p2 然后提交即可 67d709b2b54aa2aa648cf6e87a7114f1 文
  • 操作系统 线程同步实验

    操作系统 线程同步实验 一 实验目标 顺序表循环队列实现的实验目标 掌握使用顺序表和循环队列实现队列的基本操作 如队列的插入 删除 遍历等 同时了解循环队列的内部实现原理和利用循环队列解决实际问题的方法 Linux生产者 消费者问题的多线程