2019-2020-1 20175302 201752314 20175316 实验三 并发程序

实验三 并发程序-1

实验内容

学习使用Linux命令wc(1);
基于Linux Socket程序设计实现wc(1)服务器(端口号是你学号的后6位)和客户端;
客户端传一个文本文件给服务器;
服务器返加文本文件中的单词数。

设计实现

  • 命令参数
    -c:统计字节数
    -l:统计行数
    -m:统计字符数。这个标志不能与 -c 标志一起使用。
    -w:统计字数。一个字被定义为由空白、跳格或换行字符分隔的字符串
    -L:打印最长行的长度
    -help:显示帮助信息
    --version:显示版本信息

  • 实现伪代码
    int main() { fd = fopen()//打开文件; fscanf()//对文件的内容以字符串的形式进行读取 if..count++//设置条件,当满足字符串条件时计数; }
    对于wc -w功能与我们统计单词个数相似,而其实现时:由' ','\n','\t','\r'作为分隔符
    统计单词个数时,除上述分隔符,还加入了文本文件中常见的符号:'!','"','?','.',',','(',')',':',';','-'作为分隔符

  • 实现wc -w

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define BUFFERSIZE 1024

int main()
{
    FILE *fp;
    char ch;
    char filename[100];
    int flag=0,num=0;
        printf("input filename: ");
    scanf("%s",filename);
    if((fp = fopen(filename,"r"))==NULL)
    {
        printf("Failure to open %s\n",filename);
        exit(0);
    }
    while((ch=fgetc(fp))!=EOF)
    {
        if(ch==' ' || ch=='\n' || ch=='\t' || ch=='\r')
        {
            flag=0;
        }
        else
        {
            if(flag==0)
            {
                flag=1;
                num++;
            }

        }

    }
    printf("%c\n",num+48);
    fclose(fp);
    return 0;
}
  • wc txt

  • 实现单词个数统计

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define BUFFERSIZE 1024
/*int num=0;
void wc(char buffer[],int size)
{
    int i,flag=0;
    for(i=0;i<size;i++)
    {
        if(buffer[i]==' ' || buffer[i]=='\n' || buffer[i]=='\t' || buffer[i]=='\0' || buffer[i]=='!' || buffer[i]=='?' || buffer[i]=='"' || buffer[i]=='.' || buffer[i]== ',' || buffer[i]==':' || buffer[i]=='(' || buffer[i]==')' || buffer[i]==';' || buffer[i]=='■ ' || buffer[i]=='•' )
        {
            flag=0;
        }
        else
        {
            if(flag==0)
            {
                num++;
            }
            flag=1;
        }
    }
    return num;
}*/
int main()
{
    FILE *fp;
    char ch;
    char filename[100];
    int flag=0,num=0;
        printf("input filename: ");
    scanf("%s",filename);
    if((fp = fopen(filename,"r"))==NULL)
    {
        printf("Failure to open %s\n",filename);
        exit(0);
    }
    while((ch=fgetc(fp))!=EOF)
    {
        if(ch==' ' || ch=='\n' || ch=='\t' ||  ch=='\!' || ch=='\?' || ch=='\"' || ch=='\.' || ch== '\,' || ch=='\:' || ch=='\(' || ch=='\)'     || ch=='\;' || ch=='\-')
        {
            flag=0;
        }
        else
        {
            if(flag==0)
            {
                flag=1;
                num++;
            }

        }

    }
    printf("%d\n",num);
    fclose(fp);
    return 0;
}
  • 客户端和服务器的通信过程

  • 运行结果

实验三 并发程序-2

实验内容

使用多线程实现wc服务器并使用同步互斥机制保证计数正确;
对比单线程版本的性能,并分析原因。

多线程

  • 同步
    直接制约关系
    加入同步机制主要是为了在多线程程序中,如果需要对某个共享资源C进行同步访问,如果系统没有调度到B,A也是没有可能访问C的,必须等B调度到之后,A才可能重新访问。

  • 互斥锁
    主要用来保护临界资源
    临界资源,就是有可能多个线程都需要访问的数据地址,也有可能是某一段代码,执行这段代码有可能会改变多个线程都需要访问的数据。

  • 多线程实现wc服务器时,会出现多个客户端同时像服务器传送文件的情况,需要根据发送的不同文件名创建新的接收文件,并要确立好调度顺序关系

运行截图

  • 对比单线程版本的性能,并分析原因
  • 原因:所有数据结构的生存期,以及对这些数据结构的access,都用这一根逻辑线程,不需要考虑数据结构的race。把任何耗时的操作都给其他线程(IO线程、定时器线程,DB线程等)做,做完之后向事件队列(多线程安全的队列,其他线程是生产者,逻辑线程是消费者)丢事件。
  • 多线程逻辑设计的思路:
    所有数据结构的生存期,以及对这些数据结构的access,不一定在一根线程。
    需要考虑数据结构的race。
    网络事件、定时器事件唤醒工作线程(一般通过iocp或者epoll来唤醒)执行所有工作,一般不需要交换到其他线程。很显然,单线程逻辑多了一层事件队列交换,会增加延迟,以及所有的逻辑都在一根线程上跑,逻辑被阻塞也会带来延迟。其实吞吐量对于rpc来说,是个宏观的概念,尽可能快地消费网络消息就会提升吞吐量。
    对于高并发的程序,是无法忍受单线程逻辑。

实验三 并发程序-3

实验内容

  • 交叉编译多线程版本服务器并部署到实验箱中;
  • PC机作客户端测试wc服务器。

实验步骤

1.将实验箱与电脑相连,参照[实验一 开发环境的熟悉]的步骤操作,确保目标机(超级终端)和宿主机(虚拟机Ubuntu)能相互ping通(实验箱IP为192.168.0.232,Ubantu的IP为192.168.0.230)。

2.修改客户端IP段代码。
3.用交叉编译器arm-none-linux-gnuenbi-gcc编译server.c

4.参照[实验一 开发环境的熟悉],挂载共享目录,通过NFS把宿主机中的程序运行目录映射到目标机中。
5.在超级终端运行服务器armserve,在Ubantu运行客户端。

实验中遇到的问题和解决方案

  • 问题一:多线程编译中已添加头文件<pthread.h>可是编译的时候却还是报错“对pthread_create未定义的引用”。
  • 原因:pthread库不是Linux系统默认的库,连接时需要使用库libpthread.a。
  • 解决方案:所以在使用pthread_create创建线程时,在编译中要加-lpthread参数:
    gcc XXX.c -lpthread -o XXX

实验心得与体会

这次实验使我更加深刻地意识到知识之间的关联性是有多么的密切,比如这次的pthread库。它不是Linux系统默认的库,连接时需要使用静态库libpthread.a,所以在线程函数在编译时,需要使用“-lpthread”链接库函数。不经常巩固以往的知识点,它对你依旧只是陌生人。
posted on 2019-11-24 19:36  20175316  阅读(152)  评论(0编辑  收藏  举报