深入解析多线程C代码统计工具:设计与实现

引言

        在软件开发过程中,代码统计是一项基础但重要的工作,它能帮助我们了解项目规模、代码质量和维护复杂度。本文将详细介绍一个高效的多线程C代码统计工具的实现原理和技术细节。

一、工具概述

这个C语言实现的统计工具能够递归遍历指定目录,分析所有.c和.h文件,提供以下统计信息:

(1)文件总数和目录总数

(2)总代码行数

(3)有效代码行数及占比

(4)注释行数及占比

(5)空白行数及占比

二、核心数据结构设计

2.1 统计数据结构(Count)
typedef struct {
    long long total_lines;    // 总行数
    long long code_lines;     // 代码行数
    long long comment_lines;  // 注释行数
    long long blank_lines;    // 空行数
    int file_count;           // 文件数量
    int dir_count;            // 目录数量
    pthread_mutex_t lock;     // 互斥锁
} Count;

        这个结构体使用long long类型存储行数统计,确保大代码库不会溢出,同时包含互斥锁保证多线程安全。

2.2 线程参数结构(ThreadArg)
typedef struct {
    Count *count;            // 共享统计结构指针
    char path[1024];         // 文件路径
} ThreadArg;

三、关键技术实现

3.1 多线程架构

工具采用主从式(Master-Worker)多线程模型:

(1)主线程负责目录遍历和任务分发

(2)工作线程负责实际的文件行数统计

// 创建工作线程
pthread_t *tid = malloc(2000 * sizeof(pthread_t));
ThreadArg *t_a = malloc(sizeof(ThreadArg));
strncpy(t_a->path, path, sizeof(t_a->path));
t_a->count = count;
pthread_create(&tid[n], NULL, count_line, t_a);
3.2 线程同步机制
pthread_mutex_lock(&t_a->count->lock);
t_a->count->total_lines += lines;
// 其他统计量更新...
pthread_mutex_unlock(&t_a->count->lock);
3.3 递归目录遍历

采用深度优先搜索(DFS)算法递归处理子目录:

if (d->d_type == 4) {  // 目录
    count->dir_count++;
    count_file(path, count);  // 递归调用
}
3.4 代码行分析算法

文件行数统计实现了精细的代码分析:

while(fgets(buffer, size, fp)) {
    // 预处理行内容
    trim_whitespace(buffer);
    
    if(/* 空行判断 */) {
        blank_lines++;
    }
    else if(/* 多行注释开始 */) {
        comment_lines++;
        in_comment = 1;
    }
    // 其他情况处理...
}

四、性能优化策略

        (1)并行处理:每个文件独立线程处理,充分利用多核CPU

        (2)批量线程管理:预分配线程ID数组,统一等待线程结束

        (3)轻量级同步:细粒度锁只保护必要共享数据

        (4)内存高效:及时释放不再需要的资源

五、完整代码实现

#include<stdio.h>
#include<stdlib.h>
#include<dirent.h>
#include<unistd.h>
#include<pthread.h>
#include<sys/stat.h>
#include<string.h>
#include<ctype.h>

typedef struct{
    long long total_lines;
    long long code_lines;
    long long comment_lines;
    long long blank_lines;
    int file_count;
    int dir_count;
    pthread_mutex_t lock;
}Count;

typedef struct{
    Count *count;
    char path[1024];
}ThreadArg;

//计算文件的总行数
void *count_line(void *arg) {
    ThreadArg *t_a = (ThreadArg *)arg;
    FILE *fp = fopen(t_a->path, "r");
    if (!fp) {
        perror("文件打开失败");
        free(t_a);
        return NULL;
    }
    int size=4096;
    char buffer[4096];
    long long code_line=0,empty_line=0,explant_line=0;
    int in_comment=0;
    long long lines = 0;
    while(fgets(buffer, size, fp) != NULL) {  // 逐行读取文件
        lines++;  // 总行数+1
        
         // 去除行首空白
        char *p = buffer;
        while(*p == ' ' || *p == '\t') p++;
        
        // 去除行尾空白
        char *end = p + strlen(p) - 1;
        while(end >= p && (*end == ' ' || *end == '\t' || *end == '\n' || *end == '\r')) {
            end--;
        }
        *(end+1) = '\0';
        // 判断行类型
        //注释行中的空行也算作空行
        if(*p == '\0') {
            empty_line++;
        }
        else if(strstr(p, "/*")) {
            explant_line++;
            in_comment = 1;
            if(strstr(p, "*/")) in_comment = 0;
        }
        else if(in_comment) {
            explant_line++;
            if(strstr(p, "*/")) in_comment = 0;
        }
        else if(p[0] == '/' && p[1] == '/') {
            explant_line++;
        }
        else {
            code_line++;
        }
    }
    fclose(fp);

    pthread_mutex_lock(&t_a->count->lock);
    t_a->count->total_lines += lines;
    t_a->count->code_lines += code_line;
    t_a->count->comment_lines += explant_line;
    t_a->count->blank_lines += empty_line;
    pthread_mutex_unlock(&t_a->count->lock);

    free(t_a);
    return NULL;
}

// 检查是否为C源文件或头文件
int is_c_file(const char *name) {
    const char *ext = strrchr(name, '.');
    if (!ext) return 0;
    return (strcmp(ext, ".c") == 0) || (strcmp(ext, ".h") == 0);
}

//计算文件数和目录数
void count_file(char *name, Count *count) {
    pthread_t *tid=malloc(2000*sizeof(pthread_t));
    int n=0;
    DIR *dir = opendir(name);
    if (!dir) {
        perror("无法打开目录");
        return;
    }
    struct dirent *d;
    char path[1024];
    while ((d = readdir(dir)) != NULL) {
        if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0) {
            continue;
        }
        snprintf(path, sizeof(path), "%s/%s", name, d->d_name);
        if (d->d_type==8) {
            if (is_c_file(d->d_name)) {
                // 创建线程统计行数
                ThreadArg *t_a = malloc(sizeof(ThreadArg));
                strncpy(t_a->path, path, sizeof(t_a->path));
                t_a->count=count;
                pthread_create(&tid[n], NULL, count_line, t_a);
                n++;
            }
            pthread_mutex_lock(&count->lock);
            count->file_count++;
            pthread_mutex_unlock(&count->lock);
        } else if (d->d_type==4) {
            pthread_mutex_lock(&count->lock);
            count->dir_count++;
            pthread_mutex_unlock(&count->lock);
            count_file(path, count);
        }
    }
    for(int i=0;i<n;i++){
        pthread_join(tid[i],NULL);
    }
    free(tid);
    closedir(dir);
}

int main(int argc, char const *argv[])
{
    Count count={0,0,0,0,0,1};
    char *name="linux-6.15.6";    //文件名
    count_file(name,&count);
    
    printf("文件数为:%d\n",count.file_count);
    printf("文件夹数为:%d\n",count.dir_count);
    printf("文件总行数为:%lld\n",count.total_lines);

    printf("代码行数为:%lld (%.2f%%)\n", count.code_lines, 
           (double)count.code_lines * 100 / count.total_lines);
    printf("注释行数为:%lld (%.2f%%)\n", count.comment_lines, 
           (double)count.comment_lines * 100 / count.total_lines);
    printf("空行数为:%lld (%.2f%%)\n", count.blank_lines, 
           (double)count.blank_lines * 100 / count.total_lines);
    
    pthread_mutex_destroy(&count.lock);

    return 0;
}

有更高效的方法欢迎指导

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值