기사 디렉토리
멀티플렉싱
운영 체제 수준 I/O 모델은 다음과 같습니다.
- I/O 차단
- 논블로킹 I/O
- 신호 기반 I/O
- 异步I/O
- 다중 I/O
Linux에서는 모든 것이 파일입니다. 일반 파일, 디렉토리 파일, 캐릭터 디바이스 파일(키보드, 마우스), 블록 디바이스 파일(하드디스크, CD-ROM), 소켓 등을 포함한다. 파일 설명자(File descriptor, FD)는 모든 파일을 읽고 쓰는 데 사용되는 파일 리소스에 액세스하기 위한 추상 핸들입니다. 파일 설명자는 음수가 아닌 정수입니다. 각 프로세스는 기본적으로 3개의 파일 설명자를 엽니다: 0 표준 입력, 1 표준 출력 및 2 표준 오류. 메모리 제약으로 인해 파일 디스크립터에는 상한이 있으며 이는 ulimit -n을 통해 볼 수 있으며 파일 디스크립터는 다 사용한 후 제 시간에 닫아야 합니다.
I/O 차단
논블로킹 I/O
읽기 및 쓰기는 기본적으로 차단 모드입니다.
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t nbytes);
파일 설명자는 시스템 호출 fcntl을 통해 비차단 모드로 설정할 수 있습니다.
int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
다중화 I/O
선택 시스템 호출은 1024 파일 디스크립터의 읽기 가능 또는 쓰기 가능 상태를 동시에 모니터링할 수 있습니다. poll은 연결 목록을 사용하여 파일 설명자를 저장하여 1024의 상한선을 제거합니다. 각 운영 체제는 epoll, evport 및 kqueue와 같은 자체 I/O 다중화 기능을 구현합니다.
Go 다중화 기능에는 netpoll이 접두사로 붙으며 최적의 성능을 달성하기 위해 운영 체제마다 다른 패키지가 만들어집니다. Go 언어를 컴파일할 때 대상 플랫폼에 따라 컴파일을 위해 특정 분기가 선택됩니다.
카운트 다운 시작 데모를 실현하기 위해 이동 채널의 다중화를 사용하십시오.
package main
import (
"fmt"
"os"
"time"
)
//倒计时
func countDown(countCh chan int, n int, finishCh chan struct{
}) {
if n <= 0 {
//从n开始倒数
return
}
ticker := time.NewTicker(1 * time.Second) //创建一个周期性的定时器,每隔1秒执行一次
for {
countCh <- n //把n放入管道
<-ticker.C //等1秒钟
n-- //n减1
if n <= 0 {
//n减到0时退出
ticker.Stop() //停止定时器
finishCh <- struct{
}{
} //成功结束
break //退出for循环
}
}
}
//中止
func abort(ch chan struct{
}) {
buffer := make([]byte, 1)
os.Stdin.Read(buffer) //阻塞式IO,如果标准输入里没数据,该行一直阻塞。注意在键盘上敲完后要按下Enter才会把输入发给Stdin
ch <- struct{
}{
}
}
func main() {
countCh := make(chan int)
finishCh := make(chan struct{
})
go countDown(countCh, 10, finishCh) //开一个子协程,去往countCh和finishCh里放数据
abortCh := make(chan struct{
})
go abort(abortCh) //开一个子协程,去往abortCh里放数据
LOOP:
for {
//循环监听
select {
//同时监听3个channel,谁先准备好就执行谁,然后进入下一次for循环
case n := <-countCh:
fmt.Println(n)
case <-finishCh:
fmt.Println("finish")
break LOOP //退出for循环。在使用for select时,单独一个break不能退出for循环
case <-abortCh:
fmt.Println("abort")
break LOOP //退出for循环
}
}
}
제한 시간 구현
-
ctx, cancel:=context.WithCancel(context.Background()) 호출 cancel()은 ctx.Done()에 해당하는 파이프라인을 닫습니다.
-
ctx,cancel:=context.WithTimeout(context.Background(),time.Microsecond*100) 취소를 호출하거나 시간 초과에 도달하면 ctx.Done()에 해당하는 파이프라인이 닫힙니다.
-
읽기 작업은 ctx.Done() 파이프라인이 닫힌 직후에 반환됩니다.
기능 제한 시간 제어의 네 가지 구현.
package main
import (
"context"
"fmt"
"time"
)
const (
WorkUseTime = 500 * time.Millisecond
Timeout = 100 * time.Millisecond
)
//模拟一个耗时较长的任务
func LongTimeWork() {
time.Sleep(WorkUseTime)
return
}
//模拟一个接口处理函数
func Handle1() {
deadline := make(chan struct{
}, 1)
workDone := make(chan struct{
}, 1)
go func() {
//把要控制超时的函数放到一个协程里
LongTimeWork()
workDone <- struct{
}{
}
}()
go func() {
//把要控制超时的函数放到一个协程里
time.Sleep(Timeout)
deadline <- struct{
}{
}
}()
select {
//下面的case只执行最早到来的那一个
case <-workDone:
fmt.Println("LongTimeWork return")
case <-deadline:
fmt.Println("LongTimeWork timeout")
}
}
//模拟一个接口处理函数
func Handle2() {
workDone := make(chan struct{
}, 1)
go func() {
//把要控制超时的函数放到一个协程里
LongTimeWork()
workDone <- struct{
}{
}
}()
select {
//下面的case只执行最早到来的那一个
case <-workDone:
fmt.Println("LongTimeWork return")
case <-time.After(Timeout):
fmt.Println("LongTimeWork timeout")
}
}
//模拟一个接口处理函数
func Handle3() {
//通过显式sleep再调用cancle()来实现对函数的超时控制
//调用cancel()将关闭ctx.Done()对应的管道
ctx, cancel := context.WithCancel(context.Background())
workDone := make(chan struct{
}, 1)
go func() {
//把要控制超时的函数放到一个协程里
LongTimeWork()
workDone <- struct{
}{
}
}()
go func() {
//100毫秒后调用cancel(),关闭ctx.Done()
time.Sleep(Timeout)
cancel()
}()
select {
//下面的case只执行最早到来的那一个
case <-workDone:
fmt.Println("LongTimeWork return")
case <-ctx.Done(): //ctx.Done()是一个管道,调用了cancel()都会关闭这个管道,然后读操作就会立即返回
fmt.Println("LongTimeWork timeout")
}
}//LongTimeWork timeout
//模拟一个接口处理函数
func Handle4() {
//借助于带超时的context来实现对函数的超时控制
//调用cancel()或到达超时时间都将关闭ctx.Done()对应的管道
ctx, cancel := context.WithTimeout(context.Background(), Timeout)
defer cancel() //纯粹出于良好习惯,函数退出前调用cancel()
workDone := make(chan struct{
}, 1)
go func() {
//把要控制超时的函数放到一个协程里
LongTimeWork()
workDone <- struct{
}{
}
}()
select {
//下面的case只执行最早到来的那一个
case <-workDone:
fmt.Println("LongTimeWork return")
case <-ctx.Done(): //ctx.Done()是一个管道,context超时或者调用了cancel()都会关闭这个管道,然后读操作就会立即返回
fmt.Println("LongTimeWork timeout")
}
}
func main() {
Handle1()
Handle2()
Handle3()
Handle4()
}