并发、并行、进程、线程概念
- 并发(Concurrency):
-
定义: 并发是指系统能够同时处理多个任务,但不一定是同时执行。在一个并发系统中,任务可以被切分成小的时间片段,交替地执行,从而给人一种同时执行的感觉。
- 实现:可以通过多进程、多线程、协程等方式来实现并发
- 通信:为了保证数据的一致性,安全性,进程间的协调,消息传递等
- 高并发:
- 定义:服务器的处理器是有限的,但是用户请求是巨大的,这样的处理就是高并发
- 并行(Parallelism):
-
-
定义: 并行是指系统同时执行多个任务,每个任务都在不同的处理单元上独立运行。在并行系统中,多个处理器或核心同时执行不同的指令,以加快整体任务的完成速度。。
-
- 进程线程
- 进程是操作系统中一个独立的执行单元,有独立的内存空间,进程之间相互独立
- 线程是是程的一个执行单元,共享相同的内存空间和系统资源,但拥有独立的栈空间
- 实例:多个用户请求服务器。。
- 多线程: 通常,使用多线程更为常见,因为线程共享同一进程的地址空间,可以更方便地共享数据,减少了线程之间的通信和同步的复杂性。在Web服务器等场景中,多线程的模型可以通过一个主线程监听用户请求,每当有请求到达时,创建一个新的线程来处理该请求。这使得服务器能够同时服务多个用户。
- 多进程: 也可以使用多进程来处理多个用户请求,每个请求分配给一个独立的进程进行处理。这样可以实现更高度的隔离,每个进程拥有独立的内存空间,减少了进程之间的共享数据的复杂性。但进程间通信的开销相对较高,因此在某些情况下,使用多进程可能不如多线程高效。
go的并发
- 定义:主要机制是goroutine和channel(通道)
- 实现:go 关键字用于在一个新的goroutine中执行一个函数,它允许你的程序以并发的方式执行代码
- Goroutines(轻量级线程): Go语言的并发模型的核心是goroutine。Goroutine是一种轻量级的线程,由Go运行时系统管理。与传统的线程相比,goroutine的创建和销毁开销很小,因此可以创建大量的goroutine,而不会导致系统负担过重。
// 创建一个通道 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)的发送和接收操作
ch <- value // 发送 value 到通道 ch data := <-ch // 从通道 ch 中接收数据,并将其赋值给变量 data
data := <- ch <- value 从右向左
sync.WaitGroup
等待一组并发任务完成后再继续执行下一步,WaitGroup
就提供了一种简单的同步机制来实现这个目的
在线程的开始和结束,写行那两个参数
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) }
案例
案例
使用了sync.WaitGroup来等待所有的goroutine任务完成。每次启动goroutine时,通过wg.Add(1)增加WaitGroup的计数,而在createPDF函数中的defer语句中调用wg.Done()来减少计数。
这样主函数就可以通过wg.Wait()等待所有的goroutine任务完成。这样的修改使得下载和生成PDF的过程可以并发执行,提高了程序的效率
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密集型的工作负载。