并发网络编程
5.1 线程与进程
并发编程中,线程和进程是两个基本概念。线程是程序执行的最小单位,进程是程序的运行实例。线程和进程的合理使用可以提高程序的执行效率和响应速度。
5.1.1 线程的创建与管理
线程是轻量级的进程,可以共享进程的资源,如内存、文件描述符等。使用线程可以更高效地利用多核 CPU,提高并发处理能力。
示例代码:
go
package main
import (
"fmt"
"time"
)
func printNumbers() {
for i := 1; i <= 5; i++ {
fmt.Println(i)
time.Sleep(500 * time.Millisecond)
}
}
func main() {
go printNumbers()
go printNumbers()
time.Sleep(3 * time.Second)
}5.1.2 进程的创建与管理
进程是程序的执行实例,具有独立的地址空间。进程之间的通信通常需要使用 IPC(进程间通信)机制,如管道、信号、共享内存等。
示例代码(在 Unix 系统中):
go
package main
import (
"fmt"
"os"
"os/exec"
)
func main() {
cmd := exec.Command("ls", "-l")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
fmt.Println("Error running command:", err)
}
}5.2 使用线程池
线程池是一种常用的并发编程技术,通过预先创建一定数量的线程,避免频繁创建和销毁线程的开销,提高程序的执行效率。
5.2.1 线程池的概念
线程池是一组预先创建的线程,用于执行任务。线程池可以根据任务的需求动态调整线程的数量,合理分配系统资源。
5.2.2 线程池的实现
线程池的实现通常包括任务队列、工作线程和线程管理器等组件。任务队列用于存储待处理的任务,工作线程从任务队列中获取任务并执行,线程管理器负责创建、销毁和管理工作线程。
示例代码:
go
package main
import (
"fmt"
"sync"
)
type Task func()
type Worker struct {
taskQueue chan Task
wg *sync.WaitGroup
}
func NewWorker(taskQueue chan Task, wg *sync.WaitGroup) *Worker {
return &Worker{taskQueue: taskQueue, wg: wg}
}
func (w *Worker) Start() {
go func() {
for task := range w.taskQueue {
task()
w.wg.Done()
}
}()
}
type ThreadPool struct {
taskQueue chan Task
workerCount int
wg *sync.WaitGroup
}
func NewThreadPool(workerCount int) *ThreadPool {
taskQueue := make(chan Task)
wg := &sync.WaitGroup{}
pool := &ThreadPool{taskQueue: taskQueue, workerCount: workerCount, wg: wg}
for i := 0; i < workerCount; i++ {
worker := NewWorker(taskQueue, wg)
worker.Start()
}
return pool
}
func (p *ThreadPool) Submit(task Task) {
p.wg.Add(1)
p.taskQueue <- task
}
func (p *ThreadPool) Wait() {
p.wg.Wait()
close(p.taskQueue)
}
func main() {
pool := NewThreadPool(3)
for i := 1; i <= 5; i++ {
i := i
pool.Submit(func() {
fmt.Println("Task", i)
})
}
pool.Wait()
}5.3 使用协程
协程是一种比线程更轻量级的并发编程技术,通过调度器在单个或多个线程上执行协程,从而实现并发执行。Go 语言中的协程称为 goroutine。
5.3.1 协程的基本概念
协程是用户态的线程,由程序自行调度。协程切换的开销比线程和进程更低,适合用于高并发场景。
5.3.2 协程的实现
协程的实现依赖于语言或库提供的调度器,如 Go 语言的 goroutine 调度器。调度器负责管理协程的创建、切换和销毁。
示例代码:
go
package main
import (
"fmt"
"time"
)
func printNumbers() {
for i := 1; i <= 5; i++ {
fmt.Println(i)
time.Sleep(500 * time.Millisecond)
}
}
func main() {
go printNumbers()
go printNumbers()
time.Sleep(3 * time.Second)
}5.3.3 协程在网络编程中的应用
协程在网络编程中有广泛应用,如处理并发连接、实现高并发服务器等。协程可以简化并发编程模型,提高程序的可读性和维护性。
示例代码:
go
package main
import (
"bufio"
"fmt"
"net"
)
func handleConnection(conn net.Conn) {
defer conn.Close()
reader := bufio.NewReader(conn)
for {
message, err := reader.ReadString('\n')
if err != nil {
fmt.Println("Error reading:", err)
return
}
fmt.Printf("Received: %s\n", message)
response := "ACK\n"
_, err = conn.Write([]byte(response))
if err != nil {
fmt.Println("Error writing:", err)
return
}
}
}
func main() {
ln, err := net.Listen("tcp", ":8080")
if err != nil {
fmt.Println("Error listening:", err)
return
}
defer ln.Close()
for {
conn, err := ln.Accept()
if err != nil {
fmt.Println("Error accepting:", err)
continue
}
go handleConnection(conn)
}
}结论
并发网络编程通过合理使用线程、进程、线程池和协程,可以显著提高程序的并发处理能力和性能。通过本章的介绍,读者应能掌握并发网络编程的基本概念和实现方法,并应用于实际项目中。