• GO 协程【VS】C# 多线程【Go-C# Round 1】



    〇、前言

    最近接触到 Go 语言相关的内容,由于之前都是用的 C# 语言,然后就萌生了对这两种语言进行多方面比较。

    本文将在 Go 的优势项目协程,来与 C# 的多线程操作进行比较,看下差距有多大。

    实际上 C# 中也有类似协程的操作,是通过 yield 关键字实现的,等后续再另做对比。

    一、准备工作

    先准备 1000 个同样内容的 txt 文件,内容是一串简单的字符串,以供程序读取。

      

    程序实现的大体思路:

      设置固定数量的协程(Go)或线程(C#),对 1000 个文本文件循环进行操作,取到的文件中的字符串加入到字符串列表中,然后记录耗时情况。

    二、Go 语言实现

    如下代码,基于(go version go1.21.0 windows/amd64),通过有缓冲的通道 channel,来设置允许同时运行 10 个协程,并通过互斥锁来保证多协程环境中,string 切片的操作安全:

    package main
    import (
    "fmt"
    "io/fs"
    "os"
    "sync"
    "time"
    )
    var mutex sync.Mutex // 互斥锁的声明
    func main() {
    fmt.Println("begin-----------------!")
    path := "E:\\OA日常文件\\test\\gotestfile"
    files_entry, err := os.ReadDir(path) // 获取所有文件的路径
    if err != nil {
    fmt.Println("Error reading directory:", err)
    return
    }
    g := NewGoLimit(10) // max_num(最大允许并发数)设置为10
    strArray := []string{}
    var wg sync.WaitGroup
    fmt.Println("files_entry-len:", len(files_entry))
    start := time.Now()
    for _, file_entry := range files_entry {
    g.Add() // 尝试增加一个协程,若已达到最大并发数,将阻塞
    wg.Add(1)
    go func(file fs.DirEntry, g *GoLimit) {
    defer g.Done()
    defer wg.Done()
    // fmt.Println(goid.Get()) // 获取并打印 Goroutine id // 安装 goid 包:go get -u github.com/petermattis/goid
    content, err := os.ReadFile(path + "\\" + file.Name()) // 读取文件内容
    if err != nil {
    fmt.Println("Error reading file:", err)
    return
    }
    mutex.Lock() // 加锁
    strArray = append(strArray, string(content)) // 将文件内容加入数组
    mutex.Unlock() // 解锁
    time.Sleep(time.Millisecond * 10) // 模拟耗时操作
    }(file_entry, g)
    }
    wg.Wait()
    end := time.Now()
    fmt.Println("strArray-len:", len(strArray))
    fmt.Println("time.Sub(start):", end.Sub(start))
    fmt.Println("end-------------------!")
    }
    type GoLimit struct {
    ch chan int
    }
    func NewGoLimit(max int) *GoLimit {
    return &GoLimit{ch: make(chan int, max)}
    }
    func (g *GoLimit) Add() {
    g.ch <- 1
    }
    func (g *GoLimit) Done() {
    <-g.ch
    }

    三、C# 语言实现

    如下代码,基于 .NET 7.0,通过信号量 SemaphoreSlim 控制同时工作的线程数量:

    // 必要的引用
    using System;
    using System.IO;
    using System.Collections.Concurrent;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Diagnostics;
    class Program
    {
    private static SemaphoreSlim semaphore;
    static void Main(string[] args)
    {
    ConcurrentBag<string> strings = new ConcurrentBag<string>();
    int semaphorenum = 10;
    semaphore = new SemaphoreSlim(0, semaphorenum); // (初始数量,最大数量)
    string path = "E:\\OA日常文件\\test\\gotestfile";
    var files = Directory.GetFiles(path);
    semaphore.Release(semaphorenum); // 由于初始数量为 0 所以需要手动释放信号量
    Task[] tasks = new Task[1000];
    Console.WriteLine($"总信号量:{semaphorenum}");
    Console.WriteLine($"总文件数:{files.Length}");
    Stopwatch stopwatch = new Stopwatch();
    stopwatch.Start();
    for (int i = 0; i < files.Length; i++)
    {
    semaphore.Wait(); // 调用 Wait() 方法,标记等待进入信号量
    string content = File.ReadAllText(files[i]);
    var task = Task.Run(() =>
    {
    Thread.Sleep(10); // 模拟耗时操作
    try
    {
    strings.Add(content);
    }
    finally
    {
    semaphore.Release(); // 调用 Release() 方法,释放信号量
    }
    //Console.WriteLine(Thread.GetCurrentProcessorId());
    });
    tasks[i] = task;
    }
    Task.WaitAll(tasks);
    stopwatch.Stop();
    Console.WriteLine($"信号量为 {semaphorenum} 时的耗时:{stopwatch.ElapsedMilliseconds}");
    }
    }

    四、执行结果比较

    下表是运行后耗时情况对比:

    同时运行的协程/线程数 2 5 10 50 100 500 1000
    Go 耗时(ms) 7740.52 3105.82 1546.60 308.87 168.50 144.24 126.00
    C# 耗时(ms) 7865 3199 1646 1275 1291 1236 1286

    注:耗时统计均运行多次取稳定值。

    由上图可得初步结论:

    在分配的协程/线程数量较少时,两种语言的操作效率相似,随着协程/线程数量的增加,Go 较 C# 效率会明显提升。

  • 相关阅读:
    TreeSet解析
    Tkinter保姆级教程(上)
    部署一套单Master的K8s集群(kubeadm-V1.20)
    内网学习笔记(4)
    webpack学习
    UE5 C++ Interface接口
    字符串函数与内存函数讲解
    【目标检测竞赛总结】IEEE UV 2022 “Vision Meets Algae” Object Detection Challenge
    信息安全实验——网络扫描技术
    【正点原子FPGA连载】 第四章Vivado软件的安装和使用 摘自【正点原子】DFZU2EG/4EV MPSoC 之FPGA开发指南V1.0
  • 原文地址:https://www.cnblogs.com/hnzhengfy/p/18019765/go_cs_1