〇、前言

最近接触到 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# 效率会明显提升。

  

GO 协程【VS】C# 多线程【Go-C# Round 1】的更多相关文章

  1. 协程(Coroutine)与多线程,多进程

    执行多个任务可以使用多线程或多进程. 多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响 多线程中,所有变量都由所有线程共享.而线程间的切换是系统进行调度,无法控制,所以可能 一个进程中的 ...

  2. Unity学习疑问记录之协程

    http://blog.csdn.net/huang9012/article/details/38492937 总结:1.协程相当于多线程但不是,(尽管它们看上去是这样的),它们运行在同一线程中,跟普 ...

  3. Lua 协程coroutine

    协程和一般多线程的区别是,一般多线程由系统决定该哪个线程执行,是抢占式的,而协程是由每个线程自己决定自己什么时候不执行,并把执行权主动交给下一个线程. 协程是用户空间线程,操作系统其存在一无所知,所以 ...

  4. Unity中的协程(一)

    这篇文章很不错的问题,推荐阅读英文原版: Introduction to Coroutines Scripting with Coroutines   这篇文章转自:http://blog.csdn. ...

  5. [转]Unity3D协程介绍 以及 使用

    作者ChevyRay ,2013年9月28日,snaker7译  原文地址:http://unitypatterns.com/introduction-to-coroutines/ 在Unity中,协 ...

  6. Lua学习笔记(六):协程

    多线程和协程 多线程是抢占式多任务(preemptive multitasking),每个子线程由操作系统来决定何时执行,由于执行时间不可预知所以多线程需要使用同步技术来避免某些问题.在单核计算机中, ...

  7. unity3D中协程和线程混合

    这是我google unity3D一个问题偶然发现的在stackflow上非常有趣的帖子: 大意是 要在unity3D上从server下载一个zip,并解压到持久化地址.并将其载入到内存中.以下展示了 ...

  8. FastRPC 3.2 发布,高性能 C++ 协程 RPC 框架

    用过go erlang gevent的亲们应该都会知道协程在应用中带来的方便. 如果对协程不理解的同学,通过阅读下面例子可以快速了解我们框架的协程的意义,已了解的可以跳过这部分. 协程例子:假设我们要 ...

  9. 关于Unity的协程

    协程 认识协程 //协程不是多线程:是一段在主程序之外执行的代码 //协程不受生命周影响 //作用:能够口直代码在特定的时间执行. //1,延时操作 //2,等待某代码执行结束之后执行 /* 特点:1 ...

  10. python--再看并行之协程线程进程

    1.gevent协程适合I/O密集,不适合CPU密集. 3.gevent协程无法发挥多核优势,事实上,协程只是以单线程的方式在运行. 3.子程序就是协程的一种特例 项目实际应用 from gevent ...

随机推荐

  1. Laravel入坑指南(1)——Hello World

    接触PHP已经挺长一段时间了,一直对这个世界上最好的语言情有独钟.用熟练了之后,发现PHP不仅是天下第一,而且是宇宙第一.但是自从Laravel诞生之后,博主一直对Laravel有莫名的抵触,觉得这个 ...

  2. OpenStack调度器

    计算使用 nova-scheduler 服务来确定如何调度计算请求 默认配置中,调度程序会考虑以下所有条件的主机: 位于请求的可用区 (map_az_to_placement_aggregate) 放 ...

  3. spring boot+layui实现增删改查实战

    说明: 最近在做一个后台,希望用一个现成的前端模板,找了一圈发现Layui比较合适.我知道很多人都有这个需求,为了使大家快速上手,我把自己写的最实用的增删改查案例完整的展示出来. 源码地址: http ...

  4. js常用知识点整理

    说明:以下内容都是我工作中实际碰到的js知识点. 后面还会碰到其他知识点或对原来解决方案的改进,都会在本篇中持续不断的维护,希望给刚参加工作或初学的朋友一些参考. 1.给元素添加事件 $(" ...

  5. java处理json类型数据--阿里巴巴fastjson api常用方法实战

    fastjson介绍 最近工作上经常需要解析json类型数据以及java对象到json类型的互转,特地研究了下阿里巴巴的fastjson,这个是国内用的 比较多的json转换api,还有其他的入jac ...

  6. 解决idea中使用git fetch报远程仓库Authentication failed

    这个问题是项目组同事在从git服务器clone代码后做fetch操作老是报错: Authentication failed, 弹出框提示:invalid user or password 其实问题出i ...

  7. SpringBoot的自动装配原理及应用

    什么是SpringBoot自动装配 所谓的"SpringBoot自动装配"就是指:通过注解和一些简单的配置就能将某些组件载入Spring容器环境中,便于使用. 比如,很多sprin ...

  8. 海康摄像头开发笔记(一):连接防爆摄像头、配置摄像头网段、设置rtsp码流、播放rtsp流、获取rtsp流、调优rtsp流播放延迟以及录像存储

    前言   Hik防爆摄像头录像,因为防爆摄像头会有对应的APP软件,与普通的网络摄像头和球机不一样,默认认为它不可以通过web网页配置,所以弄了个来实测确认.  经测试实际上也是可以通过web网页配置 ...

  9. Anaconda下载安装步骤

    下载地址 下载比较慢的,用迅雷下,点击复制地址,然后在迅雷里面直接创建连接 Anaconda基于python3.8 Anaconda基于python3.6的 安装步骤 没啥说的,一路安装,中间有个勾选 ...

  10. 【八股cover#1】MySQL Q&A与知识点

    MySQL Q&A与知识点 1.基础知识 什么是主键? 它用来唯一标识一条记录(一个字段).每个表都必须有且只能有一个主键,主键的取值不允许为空,而且在表中必须是唯一的(当然还可以有复合主键) ...