并发、并行、进程、线程概念
- 并发(Concurrency):
-
定义: 并发是指系统能够同时处理多个任务,但不一定是同时执行。在一个并发系统中,任务可以被切分成小的时间片段,交替地执行,从而给人一种同时执行的感觉。
- 实现:可以通过多进程、多线程、协程等方式来实现并发
- 通信:为了保证数据的一致性,安全性,进程间的协调,消息传递等
- 高并发:
- 定义:服务器的处理器是有限的,但是用户请求是巨大的,这样的处理就是高并发
- 并行(Parallelism):
-
-
定义: 并行是指系统同时执行多个任务,每个任务都在不同的处理单元上独立运行。在并行系统中,多个处理器或核心同时执行不同的指令,以加快整体任务的完成速度。。
-
- 进程线程
- 进程是操作系统中一个独立的执行单元,有独立的内存空间,进程之间相互独立
- 线程是是程的一个执行单元,共享相同的内存空间和系统资源,但拥有独立的栈空间
- 实例:多个用户请求服务器。。
- 多线程: 通常,使用多线程更为常见,因为线程共享同一进程的地址空间,可以更方便地共享数据,减少了线程之间的通信和同步的复杂性。在Web服务器等场景中,多线程的模型可以通过一个主线程监听用户请求,每当有请求到达时,创建一个新的线程来处理该请求。这使得服务器能够同时服务多个用户。
- 多进程: 也可以使用多进程来处理多个用户请求,每个请求分配给一个独立的进程进行处理。这样可以实现更高度的隔离,每个进程拥有独立的内存空间,减少了进程之间的共享数据的复杂性。但进程间通信的开销相对较高,因此在某些情况下,使用多进程可能不如多线程高效。
go的并发
- 定义:主要机制是goroutine和channel(通道)
- 实现:go 关键字用于在一个新的goroutine中执行一个函数,它允许你的程序以并发的方式执行代码
- Goroutines(轻量级线程): Go语言的并发模型的核心是goroutine。Goroutine是一种轻量级的线程,由Go运行时系统管理。与传统的线程相比,goroutine的创建和销毁开销很小,因此可以创建大量的goroutine,而不会导致系统负担过重。
1 2 3 4 5 6 7 8 9 10 | // 创建一个通道 ch := make(chan int ) // 启动一个goroutine发送数据 go func() { ch <- 42 // 将数据发送到通道 }() // 在主goroutine中接收数据 data := <-ch // 从通道中接收数据 |
- Channel(通道): Goroutines之间通信是通过channel完成的。通道是一种类型,用于在goroutines之间传递数据。通道提供了同步的机制,确保一个goroutine发送的数据能够被另一个goroutine正确接收
- Select语句: Go语言的select语句用于处理多个通道操作。它使得一个goroutine可以等待多个通道操作中的任意一个完成
- Mutex(互斥锁): Go语言也提供了传统的互斥锁机制,用于在多个goroutine之间共享数据时保护共享资源的一致性
通道
<-
符号用于通道(channel)的发送和接收操作
1 2 3 | ch <- value // 发送 value 到通道 ch data := <-ch // 从通道 ch 中接收数据,并将其赋值给变量 data<br><br>data := <- <strong>ch</strong> <- value 从右向左 |
sync.WaitGroup
等待一组并发任务完成后再继续执行下一步,WaitGroup
就提供了一种简单的同步机制来实现这个目的
在线程的开始和结束,写行那两个参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | package main import ( "fmt" "sync" "time" ) func main() { var wg sync.WaitGroup for i := 1; i <= 3; i++ { wg.Add(1) go processTask(i, &wg) } // 等待所有 goroutine 完成 wg.Wait() fmt.Println( "All tasks have been completed." ) } func processTask(taskID int , wg *sync.WaitGroup) { defer wg.Done() // 模拟任务的耗时操作 time.Sleep(2 * time.Second) fmt.Printf( "Task %d has been completed.\n" , taskID) } |
案例
案例
1 | 使用了sync.WaitGroup来等待所有的goroutine任务完成。每次启动goroutine时,通过wg.Add(1)增加WaitGroup的计数,而在createPDF函数中的defer语句中调用wg.Done()来减少计数。<br>这样主函数就可以通过wg.Wait()等待所有的goroutine任务完成。这样的修改使得下载和生成PDF的过程可以并发执行,提高了程序的效率 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | package main import ( "fmt" "github.com/jung-kurt/gofpdf" "image" "image/jpeg" "io/ioutil" "net/http" "sync" ) // createPDF 通过给定的图片URL列表创建一个PDF文件 // 参数imageURLs: 包含图片URL的切片 // 参数outputPDF: 输出的PDF文件名(不包含文件扩展名) func createPDF(imageURLs []string, outputPDF string, wg *sync.WaitGroup) { // 创建一个PDF文档对象 pdf := gofpdf.New( "P" , "mm" , "Letter" , "" ) // 遍历图片URL列表 for _, imageURL := range imageURLs { // 发送HTTP请求获取图片 response, err := http.Get(imageURL) if err != nil { fmt.Printf( "Error downloading image: %v\n" , err) continue } defer response.Body.Close() // 检查HTTP响应状态码 if response.StatusCode == http.StatusOK { // 解码图片 img, _, err := image.Decode(response.Body) if err != nil { fmt.Printf( "Error decoding image: %v\n" , err) continue } // 将解码后的图片添加到PDF页面 pdf.AddPage() // 创建一个临时文件保存解码后的图片 tempFile, err := ioutil.TempFile( "" , "temp-image-*.jpg" ) if err != nil { fmt.Printf( "Error creating temp file: %v\n" , err) continue } defer tempFile.Close() // 保存解码后的图片到临时文件 jpeg.Encode(tempFile, img, nil) // 使用ImageOptions设置图像参数 opts := gofpdf.ImageOptions{ ImageType: "jpg" , ReadDpi: false, } // 将解码后的图片添加到PDF页面 pdf.ImageOptions(tempFile.Name(), 0, 0, 210, 297, false, opts, 0, "" ) } } // 添加PDF文件扩展名 outputPDF = outputPDF + ".pdf" // 将PDF保存到文件 pdf.OutputFileAndClose(outputPDF) // 通知WaitGroup任务完成 wg.Done() } func main() { for { var inputString, outputName string fmt.Print( "请输入密钥,按回车结束输入(输入 q 退出): " ) fmt.Scanln(&inputString) if inputString == "q" { break } // 格式化字符串生成一个新的字符串 baseURL := fmt.Sprintf( "https://pic03.sogoucdn.com/s3/a/201069/docTranslate/%s/transedImg" , inputString) var successURLs []string var wg sync.WaitGroup for i := 0; i < 50; i++ { numberStr := fmt.Sprintf( "%03d" , i) currentURL := baseURL + numberStr + ".jpg" response, err := http.Get(currentURL) if err != nil { fmt.Printf( "Error checking image existence: %v\n" , err) break } defer response.Body.Close() if response.StatusCode == http.StatusOK { fmt.Println( "成功" , currentURL) successURLs = append(successURLs, currentURL) } else { break } } // 增加WaitGroup的计数,表示有多少个goroutine任务需要等待 wg.Add(1) fmt.Print( "请输入翻译后的文件名: " ) fmt.Scanln(&outputName) // 启动一个goroutine执行createPDF函数 go createPDF(successURLs, outputName, &wg) // 等待所有goroutine任务完成 wg.Wait() fmt.Println( "翻译已完成" ) } } |
python的并发
- 定义:有多线程、多进程、协程
- 多线程: 在Python中,使用
threading
模块可以创建和管理线程。每个线程都是独立的执行流,但它们共享相同的全局解释器锁(Global Interpreter Lock,简称GIL)。这意味着在任何给定的时刻,只有一个线程能够执行Python字节码。因此,多线程主要用于IO密集型任务,而不适用于CPU密集型任务 - 多进程: 使用
multiprocessing
模块可以创建和管理多个进程,每个进程都有自己独立的解释器和GIL,因此可以充分利用多核处理器执行CPU密集型任务 - 协程: 协程是一种轻量级的线程,Python中通过
asyncio
模块提供了对协程的支持。协程使用async
和await
关键字,允许异步非阻塞的IO操作,从而提高程序的并发性能
python并发和go的区别
- 全局解释器锁(GIL):
- Python: Python解释器中存在GIL,它是一个全局锁,限制了同一时刻只能有一个线程执行Python字节码。多核CPU也不能充分利用其多核性能。
- Go: Go语言没有全局解释器锁,允许多个goroutines并发执行。这使得Go在处理并发和并行任务时更为高效,尤其对于CPU密集型的工作负载。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!