代码中的软件工程--以menu项目分析

一、C/C++环境配置

  1.安装Mingw,安装完成后自行加入系统环境变量,并使用命令提示符输入gcc -v指令,查看是否安装成功。

 

 

 2.VSCode配置

打开launch.json进行环境配置,主要的配置指令如下:

{
            "name": "编译",
            "type": "cppvsdbg",
            "request": "launch",
            "program": "cmd",
            "args": [
                "/C"
            ],
            "stopAtEntry": false,
            "cwd": "${workspaceFolder}",
            "environment": [],
            "externalConsole": false,
            "internalConsoleOptions" :"openOnSessionStart",
            "preLaunchTask": "C/C++: gcc.exe build active file"
        },
        
        {
            "name": "执行",
            "type": "cppvsdbg",
            "request": "launch",
            "program": "cmd",
            "args": [
                "/C",
                "${fileDirname}\\${fileBasenameNoExtension}.exe",
                "&",
                "pause"
            ],
            "stopAtEntry": false,
            "cwd": "${workspaceFolder}",
            "environment": [],
            "externalConsole": true
        }

  配置完成后我们打开hello.c文件进行测试,在VSCode左上角依次选择“编译”与“执行”按钮

 

 

 

 最终我们得到执行的结果:

 

至此,环境配置的步骤完成!

二、基于menu的软件工程分析

1.代码的模块化

  模块化程序设计是指每个小程序模块完成一个确定的功能,并在这些模块之间建立必要的联系,通过模块的互相协作完成整个功能的程序设计方法。由此可见,模块化编程强调的是逻辑和功能,而不是编程语句本身。跟常规编程不一样的是,模块化编程最大化的实现了“代码内嵌”,很多固定的和通用的代码被集成在模块内部,从而形成了类似于“黑匣子”的功能块,用户只要掌握模块化的输入输出及控制就可以很好进行应用设计。这一点非常适合初学者或者非编程专业人员。

在该项目中,lab4文件夹下头文件linktable.h、linktable.c与menu.c就体现出了代码模块化的思想,即将数据结构以及它的操作与菜单业务的进行了隔离的处理,也即将其放至于不同的文件中。

首先我们来看linktable.h头文件的关键代码:

/*
 * LinkTable Node Type
 */
typedef struct LinkTableNode
{
    struct LinkTableNode * pNext;
}tLinkTableNode;

/*
 * LinkTable Type
 */
typedef struct LinkTable
{
    tLinkTableNode *pHead;
    tLinkTableNode *pTail;
    int			SumOfNode;
    pthread_mutex_t mutex;
}tLinkTable;

/*
 * Create a LinkTable
 */
tLinkTable * CreateLinkTable();
/*
 * Delete a LinkTable
 */
int DeleteLinkTable(tLinkTable *pLinkTable);
/*
 * Add a LinkTableNode to LinkTable
 */
int AddLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode);
/*
 * Delete a LinkTableNode from LinkTable
 */
int DelLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode);
/*
 * get LinkTableHead
 */
tLinkTableNode * GetLinkTableHead(tLinkTable *pLinkTable);
/*
 * get next LinkTableNode
 */
tLinkTableNode * GetNextLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode);

  这里使用结构体构造了LinkTableNode、LinkTable两类结构,根据代码我们可知,结构分别为单向链表与双向链表,接着在此基础上完成了对函数CreateLinkTable、DeleteLinkTable、AddLinkTableNode、DelLinkTableNode、GetLinkTableHead、GetNextLinkTableNode的定义,具体实现则在linktable.c中。这些函数都是对于LinkTableNode、LinkTable结构的一系列操作,从函数名我们也可见,一个函数能够实现一项具体功能(e.g:调用CreateLinkTable函数可创建LinkTable结构),同时通过这些函数的调用,也即功能的相互组合,便能实现具体的菜单业务,这也体现出模块化思想中“一个软件模块只做一件事,只完成一个主要功能点或者一个软件特性”这一要义。

2.可重用软件模块接口

我们再一次关注linktable.h头文件,在这个文件中定义了大量的可看作接口的可重用函数,我们以其中一个为例,进行解析。

int DeleteLinkTable(tLinkTable *pLinkTable);
int DeleteLinkTable(tLinkTable *pLinkTable)
{
    if(pLinkTable == NULL)
    {
        return FAILURE;
    }
    while(pLinkTable->pHead != NULL)
    {
        tLinkTableNode * p = pLinkTable->pHead;
        pthread_mutex_lock(&(pLinkTable->mutex));
        pLinkTable->pHead = pLinkTable->pHead->pNext;
        pLinkTable->SumOfNode -= 1 ;
        pthread_mutex_unlock(&(pLinkTable->mutex));
        free(p);
    }
    pLinkTable->pHead = NULL;
    pLinkTable->pTail = NULL;
    pLinkTable->SumOfNode = 0;
    pthread_mutex_destroy(&(pLinkTable->mutex));
    free(pLinkTable);
    return SUCCESS;     
}

解析:

1.根据函数名,我们可以清晰的知道此函数用于删除一个LinkTable的链表

2.若此时需要进行删除,需要保证传入的指针pLinkTable不为空,否则会返回-1;

3.使用该接口时,需遵守tLinkTableNode及tLinkTable数据结构的定义

4.实现该接口的功能逻辑是逐项删除LinkTable中的LinkTableNode,直至为空,同时在最后将LinkTable的成员变量进行初始化。

三、线程安全

当程序同时满足以下两个条件时:

1.多个线程在操作共享的数据。
2.操作共享数据的线程代码有多条。

当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算,就会导致线程安全问题的产生。

 在该项目中许多地方也涉及到了多线程对临界资源的访问,由于访问资源临界区涉及到线程安全的问题,在项目也通过采取锁机制,实现对临界资源的互斥访问,保证线程安全。

例如linktable.c中的AddLinkTableNode函数

/*
 * Add a LinkTableNode to LinkTable
 */
int AddLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode)
{
    if(pLinkTable == NULL || pNode == NULL)
    {
        return FAILURE;
    }
    pNode->pNext = NULL;
    pthread_mutex_lock(&(pLinkTable->mutex));
    if(pLinkTable->pHead == NULL)
    {
        pLinkTable->pHead = pNode;
    }
    if(pLinkTable->pTail == NULL)
    {
        pLinkTable->pTail = pNode;
    }
    else
    {
        pLinkTable->pTail->pNext = pNode;
        pLinkTable->pTail = pNode;
    }
    pLinkTable->SumOfNode += 1 ;
    pthread_mutex_unlock(&(pLinkTable->mutex));
    return SUCCESS;		
}

  若我们想要向Linktable中添加一个LinkTableNode,函数中首先 通过代码pthread_mutex_lock(&(pLinkTable->mutex))对后面的临界区(即添加LinkTableNode)上锁,以防止多个线程同时对Linktable添加节点。当该线程对Linktable修改完成后通过函数pthread_mutex_unlock(&(pLinkTable->mutex))释放锁,允许了别的线程接着进行修改,这样实现了对临界资源的互斥访问,保证了线程的安全。

 

 

 

 

 




 

posted @ 2020-11-08 00:22  Panteng0623  阅读(148)  评论(0)    收藏  举报