多线程模型

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

本篇记录传统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
相关推荐
紧跟先前的步伐36 分钟前
Go AI Agent 实战 24|工具系统工程化收尾与阶段验收
人工智能·ai·golang·1024程序员节
日日大王学编程2 小时前
#Feed 流项目复盘:Redis 时间线缓存与 Outbox 消息可靠性设计
golang·1024程序员节
北冥you鱼9 小时前
Go 语言 CRUD 实战:从接口设计到真实业务场景
开发语言·后端·golang
利威尔xu13 小时前
Go IM 源码实战(11):token 只是个 HASH,多端互踢的顺序错了就踢错人
后端·微服务·面试·架构·golang
microrain13 小时前
SagooIoT 把协议解析和消息通知都放进了独立进程
物联网·golang·插件·sagooiot
北冥you鱼13 小时前
Go 语言 GORM 使用 Preload:从关联查询到嵌套预加载的完整实践
开发语言·jvm·golang
microrain14 小时前
把规则链存成文件之后,上线一条规则只需改一个字段
物联网·golang·规则引擎·sagooiot
SEO_juper16 小时前
Go 站点地图生成器:从首页爬全站,一键生成 sitemap.xml
xml·开发语言·人工智能·golang·node.js·seo·外贸独立站
FungLeo1 天前
成为全栈·Go 后端篇·跨语言认证兼容:Node 与 Go 如何互认 JWT 和密码
golang·go·jwt·身份认证·成为全栈·bcrypt