多线程模型

记录一下多线程模型,最近有相关 内容的开发,做一下简单总结

本篇记录传统select/poll

这是一个简单的demo,模仿select/poll 原理

Go 复制代码
// 用Go模拟select的用法
package main

import (
    "fmt"
    "net"
    "os"
    "syscall"
)

func main() {
    // 创建3种不同的文件描述符
    
    // 1. TCP连接
    conn, _ := net.Dial("tcp", "google.com:80")
    tcpFd := getFd(conn)
    
    // 2. 标准输入(终端)
    stdinFd := os.Stdin.Fd()
    
    // 3. 管道
    pipeR, pipeW, _ := os.Pipe()
    pipeFd := pipeR.Fd()
    
    // 准备监控这些fd
    var readFds syscall.FdSet
    FD_SET(tcpFd, &readFds)
    FD_SET(stdinFd, &readFds)
    FD_SET(pipeFd, &readFds)
    
    // 找出最大的fd编号
    maxFd := int(pipeFd)
    if tcpFd > maxFd {
        maxFd = int(tcpFd)
    }
    if stdinFd > maxFd {
        maxFd = int(stdinFd)
    }
    
    // 等待任何fd有数据
    n, err := syscall.Select(maxFd+1, &readFds, nil, nil, nil)
    if err != nil {
        panic(err)
    }
    
    fmt.Printf("有 %d 个文件描述符就绪\n", n)
    
    // 检查哪个fd有数据
    if FD_ISSET(tcpFd, &readFds) {
        fmt.Println("网络数据到达")
        // 读取socket数据,不会阻塞
    }
    
    if FD_ISSET(stdinFd, &readFds) {
        fmt.Println("键盘输入到达")
        // 读取终端输入,不会阻塞
    }
    
    if FD_ISSET(pipeFd, &readFds) {
        fmt.Println("管道数据到达")
        // 读取管道数据,不会阻塞
    }
}

func FD_SET(fd uintptr, set *syscall.FdSet) {
    set.Bits[fd/64] |= 1 << (fd % 64)
}

func FD_ISSET(fd uintptr, set *syscall.FdSet) bool {
    return (set.Bits[fd/64] & (1 << (fd % 64))) != 0
}

func getFd(conn net.Conn) uintptr {
    tcpConn := conn.(*net.TCPConn)
    file, _ := tcpConn.File()
    return file.Fd()
}

上面这个只适合旧版本的golang,新版本syscall.Select会被弃用,而且还有语法错误,mac环境

windows + go1.20如下

Go 复制代码
// 用Go模拟select的用法 - 跨平台版本
package main

import (
	"fmt"
	"io"
	"net"
	"os"
	"time"
)

func main() {
	// 创建3种不同的可读源

	// 1. TCP连接
	conn, err := net.DialTimeout("tcp", "google.com:80", 5*time.Second)
	if err != nil {
		fmt.Printf("连接失败: %v\n", err)
		conn = nil
	} else {
		defer conn.Close()
		// 发送一个简单的HTTP请求
		fmt.Fprintf(conn, "GET / HTTP/1.0\r\n\r\n")
	}

	// 2. 标准输入(使用channel模拟)
	stdinCh := make(chan []byte)
	go func() {
		buf := make([]byte, 1024)
		n, err := os.Stdin.Read(buf)
		if err == nil {
			stdinCh <- buf[:n]
		} else if err != io.EOF {
			close(stdinCh)
		}
	}()

	// 3. 管道(使用channel模拟)
	pipeCh := make(chan []byte)
	go func() {
		// 模拟2秒后管道有数据
		time.Sleep(2 * time.Second)
		pipeCh <- []byte("hello from pipe\n")
	}()

	// 使用 Go 的 select 语句来监控多个 channel
	fmt.Println("等待数据到达...")
	fmt.Println("提示:你可以在2秒内输入一些内容,或等待管道数据到达")
	fmt.Println()

	// 设置超时
	timeout := time.After(10 * time.Second)

	select {
	case data := <-stdinCh:
		fmt.Println("✓ 键盘输入到达")
		fmt.Printf("  键盘输入: %s", data)

	case data := <-pipeCh:
		fmt.Println("✓ 管道数据到达")
		fmt.Printf("  管道数据: %s", data)

	case <-timeout:
		fmt.Println("✗ 超时,没有数据到达")
	}

	// 检查网络连接是否有数据(非阻塞)
	if conn != nil {
		// 设置1秒超时读取
		conn.SetReadDeadline(time.Now().Add(1 * time.Second))
		buf := make([]byte, 1024)
		n, err := conn.Read(buf)
		if err == nil && n > 0 {
			fmt.Println("✓ 网络数据到达")
			fmt.Printf("  网络数据: %s\n", buf[:n])
		} else if err != nil {
			fmt.Printf("网络读取: %v\n", err)
		}
	}
}
Go 复制代码
连接失败: dial tcp 142.250.69.174:80: i/o timeout
等待数据到达...
提示:你可以在2秒内输入一些内容,或等待管道数据到达

✓ 管道数据到达
  管道数据: hello from pipe
相关推荐
ruxingli7 小时前
GoLang channel管道
开发语言·后端·golang
_DCG_8 小时前
go第一个工程安装过程与问题汇总
开发语言·后端·golang
onlywhz9 小时前
GO 快速升级Go版本
开发语言·redis·golang
童话ing10 小时前
【Golang】sync.Map底层原理解析
开发语言·后端·golang
ん贤10 小时前
AI 大模型落地系列|Eino 组件核心篇:文档进入 RAG 之前,Loader 和 Parser 到底各管什么
人工智能·ai·golang·eino
人间打气筒(Ada)10 小时前
Go RPC 如何实现服务间通信
开发语言·rpc·golang·远程调用·go rpc
lars_lhuan19 小时前
Go WaitGroup 源码解析
golang
人间打气筒(Ada)1 天前
如何基于 Go-kit 开发 Web 应用:从接口层到业务层再到数据层
开发语言·后端·golang
想搞艺术的程序员1 天前
Go RWMutex 源码分析:一个计数器,如何把“读多写少”做得又快又稳
开发语言·redis·golang