C/C++开发,无可避免的多线程(篇五).实现自己的线程封装类

news2024/9/21 17:55:06

一、c++11以前的线程封装版本

        在本专栏的多线程课题第一篇就说过,c++11标准前,实现多线程事务是由调用的<pthread.h>头文件的线程相关功能函数来实现的。

        现在通过<pthread.h>的pthread_create、pthread_join、pthread_exit等功能函数来封装一个线程类MyThread:

        1)MyThread可以被继承,具有run线程函数,该函数为虚函数,可以被派生类重写

        2)线程类启动start、停止stop的成员函数,可以管理线程线程函数运行或退出(run函数)能力

        3)可以获得线程的状态、线程编号相关信息。

        1.1 mythread_c98实现设计

        为此,本博文创建mythread_c98.h头文件,设计该线程类MyThread内容如下:

#ifndef _MYTHREAD_C98_H_
#define _MYTHREAD_C98_H_

#include <pthread.h>    //pthread_t
#include <unistd.h>     //usleep

class MyThread
{
public:
    // constructed function
    MyThread();
    virtual ~MyThread();
    //the entity for thread running
    virtual int run()=0;

    //start thread
    bool start();
    //wait for thread end in limit time(ms)
    void stop(unsigned long millisecondTime=0);

    //get thread status
    int state();
    //gte thread ID
    pthread_t getThreadID();
private:
    //current thread ID
    pthread_t tid;
    //thread status
    enum THREAD_STATUS
    {
        THREAD_STATUS_NEW       = 0,      //threadStatus-new create
        THREAD_STATUS_RUNNING   = 1,  //threadStatus-running
        THREAD_STATUS_EXIT      = -1     //threadStatus-end
    };
    THREAD_STATUS threadStatus;
    //get manner pointer of execution 
    static void* run0(void* pVoid);
    //manner of execution inside
    void* run1();
    //thread end
    void join();
};

#endif /* _MYTHREAD_C98_H_*/

        线程类通过一个枚举THREAD_STATUS设置线程的开启、运行、结束三个状态。start函数会调用pthread_create新创建一个线程,并调用run函数,而stop函数会调用pthread_join停止线程,线程类析构函数则调用pthread_exit来让线程销毁,具体实现如下:

#include "myThread_c98.h"

#include <stdio.h>

void* MyThread::run0(void* pVoid)
{
    MyThread* p = (MyThread*) pVoid;
    p->run1();
    return p;
}

void* MyThread::run1()
{
    threadStatus = MyThread::THREAD_STATUS_RUNNING;
    tid = pthread_self();
    printf("thread is run, tid(%d)!\n",tid);
    this->run();
    return NULL;
}

MyThread::MyThread()
{
    tid = 0;
    threadStatus = MyThread::THREAD_STATUS_NEW;
}

MyThread::~MyThread()
{
    printf("thread is ~MyThread!\n");
    threadStatus = MyThread::THREAD_STATUS_EXIT;
    tid = 0;
    pthread_exit(NULL);
}

int MyThread::run()
{
    while( MyThread::THREAD_STATUS_RUNNING == threadStatus )
    {
        printf("thread is running!\n");
        sleep(10);
    }
    return 0;
}

bool MyThread::start()
{
    return pthread_create(&tid, NULL, run0, this) == 0;
}

pthread_t MyThread::getThreadID()
{
    return tid;
}

int MyThread::state()
{
    return (int)threadStatus;
}

void MyThread::join()
{
    threadStatus = MyThread::THREAD_STATUS_EXIT;
    if (tid > 0)
    {
        void * pth_join_ret = NULL;
        int ret = pthread_join(tid, &pth_join_ret);
        printf("thread is join, tid(%d)!\n",tid);
        if(0!=ret)
            printf("pthread_join tid(%d) error:%s\n",(char*)pth_join_ret);
        pthread_cancel(tid);
        tid = 0;
    }
}
//等待millisecondTime(ms)后关闭线程
void MyThread::stop(unsigned long millisecondTime)
{
    if (tid == 0)
    {
        return;
    }else{
        printf("thread is join tid(%d) for time(%lu)!\n",tid,millisecondTime);
        unsigned long k = 0;
        while (k < millisecondTime)
        {
            usleep(1000);//等待1毫秒
            k++;
        }
        join();
    }
}

        PS,pthread_exit一般是子线程调用,用来结束当前线程,一般在该线程中自己调用退出采用pthread_exit函数;pthread_join一般是主线程来调用,用来等待子线程退出,因为是等待,所以是阻塞的。外部线程调用该线程做退出处理,采用pthread_join函数来实现。

        1.2 MyThread派生类-真正业务实现

        因为MyThread的run函数是虚函数,因此不能直接使用MyThread创建线程对象,而是需要先创建MyThread类的派生类,通过派生类重新实现run函数(即实际任务实现),再通过派生构建线程对象来使用。

        下来看看如何应用MyThread线程类。创建test1.h、test1.cpp源文件,创建MyThread类的派生类MyThreadTest如下:

//test1.h
#ifndef _TEST_1_H_
#define _TEST_1_H_
#include "mythread_c98.h"

class MyThreadTest : public MyThread
{
public:
    int run();
};
#endif
//test1.cpp
#include "test1.h"
#include <stdio.h>

int MyThreadTest::run()
{
    int index_test = 0;
    while (1==this->state())
    {
        printf("MyThreadTest is %d running!\n",++index_test);
        usleep(1000);
    }
    return 0;
};

        派生类MyThreadTest仅仅是重写了run函数,这里的任务是没间隔1毫秒打印一句文字。

        1.3 线程类调用测试

        然后将在main.cpp函数中使用该派生类。

//main.cpp
#include "test1.h"
#include <stdio.h>

int main(int argc, char* argv[])
{
    MyThreadTest th;
    th.start();
    th.stop(10);
    sleep(1);
    th.start();
    th.stop(10);
    printf("main is finish!\n");
    return 0;
};

        上述这段代码,实现了线程两次启停测试,每次是一旦启动start后,立刻调用stop去延时10毫秒结束线程,前后两次测试等待了1秒时间。现在编译测试一下(相关编译环境搭建可以看参看前面博文:C/C++开发,无可避免的多线程(篇一).跨平台并行编程姗姗来迟_py_free-物联智能的博客-CSDN博客),首选是win下,采用的MinGW编译工具,编译时特别指定了采用c++98标准,g++ main.cpp mythread_c98.cpp test1.cpp -o test.exe -std=c++98,是正确达成期望的运行结果:

         然后也在Linux编译环境测试了一下,本博文采用的是cygwin64编译工具,同样达到一样的效果。

        有了上述这个线程封装类,在很多小项目编程中就可以直接采用它派生一个子类,将要实现的业务在run重写,需要的话,可以通过线程状态和子类成员变量共同判断循环条件,就可以快速实现自己的多线程了。另外使用完该线程,也直接析构也达到停止及退出循环。

 二、c++11后的线程封装版本

         2.1 mythread_c11线程实现

        下来采用c++11标准库里的线程支持库来实现和上面示例代码基本一致的功能。为了和前面保持一致,先将前面的mythread_c98.h和mythread_c98.cpp拷贝一份,重命名为mythread_c11.h和mythread_c11.cpp源文件,mythread_c11.h调整内容如下,就做了三处微调,代码中已经标注出来:

#ifndef _MYTHREAD_C11_H_
#define _MYTHREAD_C11_H_

#include <thread>       //std::thread,c11时调整部分
#include <unistd.h>     //usleep

class MyThread
{
public:
    // constructed function
    MyThread();
    virtual ~MyThread();
    //the entity for thread running
    virtual int run()=0;

    //start thread
    bool start();
    //wait for thread end in limit time(ms)
    void stop(unsigned long millisecondTime=0);

    //get thread status
    int state();
    //gte thread ID
    std::thread::id getThreadID(); //c11时调整部分
private:
    //current thread ID
    std::thread* tid;           //c11时调整部分
    //thread status
    enum THREAD_STATUS
    {
        THREAD_STATUS_NEW       = 0,     //threadStatus-new create
        THREAD_STATUS_RUNNING   = 1,     //threadStatus-running
        THREAD_STATUS_EXIT      = -1     //threadStatus-end
    };
    THREAD_STATUS threadStatus;
    //get manner pointer of execution 
    static void* run0(void* pVoid);
    //manner of execution inside
    void* run1();
    //thread end
    void join();
};

#endif /* _MYTHREAD_C11_H_ */

        然后再来看线程类的实现部分,无非就是将 c++98支持的pthread_create、pthread_join、pthread_exit等功能函数替换成c++11的std::thread来实现。

#include "myThread_c11.h"

#include <stdio.h>

void* MyThread::run0(void* pVoid)
{
    MyThread* p = (MyThread*) pVoid;
    p->run1();
    return p;
}

void* MyThread::run1()
{
    threadStatus = MyThread::THREAD_STATUS_RUNNING;
    printf("thread is run, tid(%d)!\n",tid->get_id());
    this->run();
    return NULL;
}

MyThread::MyThread()
{
    tid = nullptr;
    threadStatus = MyThread::THREAD_STATUS_NEW;
}

MyThread::~MyThread()
{
    printf("thread is ~MyThread!\n");
    threadStatus = MyThread::THREAD_STATUS_EXIT;
    delete tid;
    tid = nullptr;
}

int MyThread::run()
{
    while( MyThread::THREAD_STATUS_RUNNING == threadStatus )
    {
        printf("thread is running!\n");
        sleep(10);
    }
    return 0;
}

bool MyThread::start()
{
    tid = new std::thread(run0,this);
    return (nullptr!=tid);
}

std::thread::id MyThread::getThreadID()
{
    if(nullptr==tid)
        return std::thread::id();
    return tid->get_id();
}

int MyThread::state()
{
    return (int)threadStatus;
}

void MyThread::join()
{
    threadStatus = MyThread::THREAD_STATUS_EXIT;
    if (nullptr!=tid)
    {
        tid->join();
        delete tid;
        tid = nullptr;
    }
}
//等待millisecondTime(ms)后关闭线程
void MyThread::stop(unsigned long millisecondTime)
{
    if (tid == 0)
    {
        return;
    }else{
        printf("thread is join tid(%d) for time(%lu)!\n",tid->get_id(),millisecondTime);
        unsigned long k = 0;
        while (k < millisecondTime)
        {
            usleep(1000);//等待1毫秒
            k++;
        }
        join();
    }
}

        上述代码相对c++98的实现几乎没什么变化,就是原来涉及到pthread_create、pthread_join、pthread_exit的start函数、析构函数、join函数做了调整,原来tid是采用0来初始赋值的,现在转用了c++11支持的nullptr,其他地方几乎保持不变,打印输出的线程id通过std::thread的成员函数tid->get_id()来实现。

        2.2 mythread_c11线程调用测试

        然后再看调用方面,还是采用前面的派生类及调用例子,test1.h和test1.cpp,就仅仅需要修改一下test1.h对头文件的引用,其他保持不变:

//test1.h
#ifndef _TEST_1_H_
#define _TEST_1_H_
//#include "mythread_c98.h"//注释
#include "mythread_c11.h"//采用c++11的实现

class MyThreadTest : public MyThread
{
public:
    int run();
};
#endif
//test1.cpp
#include "test1.h"
#include <stdio.h>

int MyThreadTest::run()
{
    int index_test = 0;
    while (1==this->state())
    {
        printf("MyThreadTest is %d running!\n",++index_test);
        usleep(1000);
    }
    return 0;
};

        测试调用代码main.cpp全部不变:

#include "test1.h"
#include <stdio.h>

int main(int argc, char* argv[])
{
    MyThreadTest th;
    th.start();
    th.stop(10);
    sleep(1);
    th.start();
    th.stop(10);
    printf("main is finish!\n");
    return 0;
};

        编译指令:g++ main.cpp mythread_c11.cpp test1.cpp -o test.exe -std=c++11,运行测试成如下,同样达到目的,咋不说,使用的人根本不知道内部做了调整:

         同样linux编译环境也测试一下,OK:

         其实上述代码还可以进一步优化,枚举类型的线程状态毕竟涉及到本线程及可能外部线程调用,因此如果采用原子类型std::atomic来表述也不错的,可以解决几乎不会触发的线程数据安全问题。

三、合而为一

        很多时候,在实际项目中,有些模板使用c++98标准库,有些模块使用c++11以上标准库,虽然说支持c++98的在c++11编译环境也是OK的:g++ main.cpp mythread_c98.cpp test1.cpp -o test.exe -std=c++11。但是如此相似的两个线程类版本,是很容易合并在一起的,通过宏编译条件,c++98和c++11两不误。

        3.1 mythread混合版本实现

        再次拷贝一份mythread_c98.h、mythread_c98.cpp并重命名为mythread.h、mythread.cpp源文件,然后根据__cplusplus宏进行条件编译设置:

        mythread.h内容如下,和前面c98、c11的相比,就是根据__cplusplus宏定义一些c98、c11编译环境下归一化别名:

#ifndef _MYTHREAD_H_
#define _MYTHREAD_H_

#if __cplusplus < 201103L
#include <pthread.h>        //pthread_t
#define nullptr 0
typedef pthread_t ThreadID;
typedef pthread_t RThreadID;
#else
#include <thread>           //std::thread,c11时调整部分
typedef std::thread* ThreadID;
typedef std::thread::id RThreadID;
#endif
#include <unistd.h>         //usleep

class MyThread
{
public:
    // constructed function
    MyThread();
    virtual ~MyThread();
    //the entity for thread running
    virtual int run()=0;

    //start thread
    bool start();
    //wait for thread end in limit time(ms)
    void stop(unsigned long millisecondTime=0);

    //get thread status
    int state();
    //get thread ID
    RThreadID getThreadID();    //调整
private:
    //current thread ID
    ThreadID tid;        //调整
    //thread status
    enum THREAD_STATUS
    {
        THREAD_STATUS_NEW       = 0,      //threadStatus-new create
        THREAD_STATUS_RUNNING   = 1,  //threadStatus-running
        THREAD_STATUS_EXIT      = -1     //threadStatus-end
    };
    THREAD_STATUS threadStatus;
    //get manner pointer of execution 
    static void* run0(void* pVoid);
    //manner of execution inside
    void* run1();
    //thread end
    void join();
};

         mythread.cpp,定义文件内容可能会麻烦一下,需要将根据宏条件改写c98和c11不同之处的代码,但前面都曾实现过,仅仅麻烦下,倒不难。

#include "myThread.h"

#include <stdio.h>

void* MyThread::run0(void* pVoid)
{
    MyThread* p = (MyThread*) pVoid;
    p->run1();
    return p;
}

void* MyThread::run1()
{
    threadStatus = MyThread::THREAD_STATUS_RUNNING;
    #if __cplusplus < 201103L
    tid = pthread_self();
    printf("thread is run, tid(%d)!\n",tid);
    #else
    printf("thread is run, tid(%d)!\n",tid->get_id());
    #endif
    this->run();
    return nullptr;
}

MyThread::MyThread()
{
    tid = nullptr;
    threadStatus = MyThread::THREAD_STATUS_NEW;
}

MyThread::~MyThread()
{
    printf("thread is ~MyThread!\n");
    threadStatus = MyThread::THREAD_STATUS_EXIT;
    #if __cplusplus < 201103L
    pthread_exit(NULL);
    #else
    delete tid;
    #endif
    tid = nullptr;  
}

int MyThread::run()
{
    while( MyThread::THREAD_STATUS_RUNNING == threadStatus )
    {
        printf("thread is running!\n");
        sleep(10);
    }
    return 0;
}

bool MyThread::start()
{
    #if __cplusplus < 201103L
    return pthread_create(&tid, NULL, run0, this) == nullptr;
    #else
    return (nullptr!=(tid = new std::thread(run0,this)));
    #endif
    
}

RThreadID MyThread::getThreadID()
{
    #if __cplusplus < 201103L
    return tid;
    #else
    return (nullptr==tid)?std::thread::id():tid->get_id();
    #endif
}

int MyThread::state()
{
    return (int)threadStatus;
}

void MyThread::join()
{
    threadStatus = MyThread::THREAD_STATUS_EXIT;
    if (nullptr!=tid)
    {
        #if __cplusplus < 201103L
        int ret = pthread_join(tid, nullptr);
        pthread_cancel(tid);
        #else
        tid->join();
        delete tid;
        #endif
        tid = nullptr;
    }
}
//等待millisecondTime(ms)后关闭线程
void MyThread::stop(unsigned long millisecondTime)
{
    if (nullptr == tid)
    {
        return;
    }else{
        #if __cplusplus < 201103L
        printf("thread is join tid(%d) for time(%lu)!\n",tid,millisecondTime);
        #else
        printf("thread is join tid(%d) for time(%lu)!\n",tid->get_id(),millisecondTime);
        #endif
        unsigned long k = 0;
        while (k < millisecondTime)
        {
            usleep(1000);//等待1毫秒
            k++;
        }
        join();
    }
}

        这样就达成了c++98和c++11标准库版本下的统一。

        3.2 混合版本调用及编译测试

        还是利用前面的测试例子进行测试一下,调整test1.h的头文件应用,其保持不变:

//test1.h
#ifndef _TEST_1_H_
#define _TEST_1_H_
// #include "mythread_c98.h"
// #include "mythread_c11.h"
#include "mythread.h"

class MyThreadTest : public MyThread
{
public:
    int run();
};
#endif
//test1.cpp
#include "test1.h"
#include <stdio.h>

int MyThreadTest::run()
{
    int index_test = 0;
    while (1==this->state())
    {
        printf("MyThreadTest is %d running!\n",++index_test);
        usleep(1000);
    }
    return 0;
};

        线程的调用在main.cpp内,保持原来一致,也不需要作出任何调整:

#include "test1.h"
#include <stdio.h>

int main(int argc, char* argv[])
{
    MyThreadTest th;
    th.start();
    th.stop(10);
    sleep(1);
    th.start();
    th.stop(10);
    printf("main is finish!\n");
    return 0;
};

        再次编译,可以指定标准库版本,也可以不指定,都能得到正确编译结果:

        1)g++ main.cpp mythread.cpp test1.cpp -o test.exe -std=c++98

        2)g++ main.cpp mythread.cpp test1.cpp -o test.exe -std=c++11

        3)g++ main.cpp mythread.cpp test1.cpp -o test.exe

        运行编译输出程序:

        这样一个基本的满足c++98、c++11标准库混合编译环境的线程类就做好了,在一般小型项目内,通过继承它,做一些简要调整就能快速应用多多线程开发中。

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

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

相关文章

VRRP主备备份

1、VRRP专业术语 VRRP备份组框架图如图14-1所示: 图14-1:VRRP备份组框架图 VRRP路由器(VRRP Router):运行VRRP协议的设备,它可能属于一个或多个虚拟路由器,如SwitchA和SwitchB。虚拟路由器(Virtual Router):又称VRR…

元宇宙、区块链 通俗易懂

什么是区块链&#xff1f;比特币挖矿是什么&#xff1f;元宇宙是什么&#xff1f;Web(万维网)的三权化进化&#xff1a;基于此&#xff0c;介绍下“元宇宙”。1992年&#xff0c;美国作家史蒂芬森在《雪崩》一书中首次提出了“元宇宙(Metaverse)”的概念。元宇宙实际上就是一种…

新C++(13):布隆过滤器

"明白成功&#xff0c;不一定赢在起跑线!"位图反思上篇呢&#xff0c;我们在遇到海量数据时&#xff0c;如果只是进行诸如&#xff0c;查找一个数在不在这样的简单逻辑情况&#xff0c;在使用数组这样的内存容器&#xff0c;无法存储这么多数据时&#xff0c;我们采用…

计算机网络第八版——第三章课后题答案(超详细)

第三章 该答案为博主在网络上整理&#xff0c;排版不易&#xff0c;希望大家多多点赞支持。后续将会持续更新&#xff08;可以给博主点个关注~ 第一章 答案 第二章 答案 【3-01】数据链路&#xff08;即逻辑链路&#xff09;与链路&#xff08;即物理链路&#xff09;有何区…

Numpy/Pandas常用函数

&#x1f442; 不露声色 - Jam - 单曲 - 网易云音乐 目录 &#x1f33c;前言 &#x1f44a;一&#xff0c;Python列表函数 &#x1f44a;二&#xff0c;Numpy常用函数 1&#xff0c;生成数组 2&#xff0c;描述数组属性 3&#xff0c;常用统计函数 4&#xff0c;矩阵运…

Soul 云原生网关最佳实践

作者&#xff1a;Soul 运维 公司介绍 Soul 是基于兴趣图谱和游戏化玩法的产品设计&#xff0c;属于新一代年轻人的虚拟社交网络。成立于2016年&#xff0c;Soul 致力于打造一个“年轻人的社交元宇宙”&#xff0c;最终愿景是“让天下没有孤独的人”。在 Soul&#xff0c;用户…

springboot复习(黑马)(持续更新)

学习目标基于SpringBoot框架的程序开发步骤熟练使用SpringBoot配置信息修改服务器配置基于SpringBoot的完成SSM整合项目开发一、SpringBoot简介1. 入门案例问题导入SpringMVC的HelloWord程序大家还记得吗&#xff1f;SpringBoot是由Pivotal团队提供的全新框架&#xff0c;其设计…

为什么低代码最近又火了起来?是钉钉的原因吗?

为什么低代码最近又火了起来&#xff1f;是钉钉的原因吗&#xff1f; 钉钉的入局固然推动了人们对于低代码的讨论&#xff0c;但低代码由来已久&#xff0c;其火爆其实是大势所趋。 那么本篇文章将来解读一下&#xff1a;为什么低代码最近又火了&#xff1f;是资本的推动还是…

佩戴舒适的蓝牙耳机有哪些?佩戴舒适的蓝牙耳机推荐

音乐对许多人而言&#xff0c;都是一种抚慰生命的力量&#xff0c;特别是在上下班的时候&#xff0c;在熙熙攘攘的人流中&#xff0c;戴着耳机听一首动听的曲子&#xff0c;无疑会让人心情变得更加舒畅&#xff0c;要想获得出色的音乐体验&#xff0c;没有一副出色的耳机可不行…

动态内存基础(三)

动态内存的相关问题 ● sizeof 不会返回动态分配的内存大小 #include<iostream> #include<new> #include<memory> #include<vector> int main(int argc, char *argv[]) {int* ptr new int(3);std::cout << sizeof(ptr) << std::endl; //…

阶段式/瀑布完整软件研发流程

软件产品开发流程&#xff1a;下图所示的是一个软件产品开发大体上所需要经历的全部流程&#xff1a;编辑1、启动在项目启动阶段&#xff0c;主要确定项目的目标及其可行性。我们需要对项目的背景、干系人、解决的问题等等进行了解。并编制项目章程和组建项目团队&#xff0c;包…

STM32实战项目-状态机函数应用

前言&#xff1a; 本章主要介绍一下&#xff0c;状态机在工程中的应用&#xff0c;下面我会通过这种方式点亮LED灯&#xff0c;来演示他的妙用。 目录 1、状态机应用 1.1流水灯函数 1.1.1led.h 1.1.2led.c 1.2状态机函数 1.2.1定义举常量 1.2.2结构体封装 1、状态机应…

设计模式-01

1&#xff0c;设计模式概述 1.1 软件设计模式的产生背景 "设计模式"最初并不是出现在软件设计中&#xff0c;而是被用于建筑领域的设计中。 1977年美国著名建筑大师、加利福尼亚大学伯克利分校环境结构中心主任克里斯托夫亚历山大&#xff08;Christopher Alexand…

VUE3使用JSON编辑器

1、先看看效果图&#xff0c;可以自行选择展示效果 2、这是我在vue3项目中使用的JSON编辑器&#xff0c;首先引入第三方插件 npm install json-editor-vue3yarn add json-editor-vue33、引入到项目中 // 导入模块 import JsonEditorVue from json-editor-vue3// 注册组件 …

【pytorch onnx】Pytorch导出ONNX及模型可视化教程

文章目录1 背景介绍2 实验环境3 torch.onnx.export函数简介4 单输入网络导出ONNX模型代码实操5 多输入网络导出ONNX模型代码实操6 ONNX模型可视化7 ir_version和opset_version修改8 致谢原文来自于地平线开发者社区&#xff0c;未来会持续发布深度学习、板端部署的相关优质文章…

RocketMQ5.1控制台的安装与启动

RocketMQ控制台的安装与启动下载修改配置开放端口号重启防火墙添加依赖编译 rocketmq-dashboard运行 rocketmq-dashboard本地访问rocketmq无法发送消息失败问题。connect to &#xff1c;公网ip:10911&#xff1e; failed下载 下载地址 修改配置 修改其src/main/resources中…

【操作系统原理实验】银行家算法模拟实现

选择一种高级语言如C/C等&#xff0c;编写一个银行家算法的模拟实现程序。1) 设计相关数据结构&#xff1b;2) 实现系统资源状态查看、资源请求的输入等模块&#xff1b;3) 实现资源的预分配及确认或回滚程序&#xff1b;4) 实现系统状态安全检查程序&#xff1b;5) 组装各模块…

TCP模拟HTTP请求

HTTP的特性HTTP是构建于TCP/IP协议之上&#xff0c;是应用层协议&#xff0c;默认端口号80HTTP协议是无连接无状态的HTTP报文请求报文HTTP协议是以ASCⅡ码传输&#xff0c;建立在TCP/IP协议之上的应用层规范。HTTP请求报文由请求行&#xff08;request line&#xff09;、请求头…

Flutter 自定义今日头条版本的组件,及底部按钮切换静态样式

这里写目录标题1. 左右滑动实现标题切换&#xff0c;点击标题也可实现切换&#xff1b;2. 自定义KeepAliveWrapper 缓存页面&#xff1b;2.2 使用3. 底部导航切换&#xff1b;4. 自定义中间大导航&#xff1b;5.AppBar自定义顶部按钮图标、颜色6. Tabbar TabBarView实现类似头条…

iOS开发之UIStackView基本运用

UIStackView UIStackView是基于自动布局AutoLayout&#xff0c;创建可以动态适应设备方向、屏幕尺寸和可用空间的任何变化的用户界面。UIStackView管理其ArrangedSubview属性中所有视图的布局。这些视图根据它们在数组中的顺序沿堆栈视图的轴排列。由axis, distribution, align…