goroutine和chan

2022/3/9 6:14:55

本文主要是介绍goroutine和chan,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

package main

import (
	"errors"
	"fmt"
	"reflect"
	"sync"
	"syscall"
	"unsafe"
)

var wg sync.WaitGroup
var once sync.Once

type Str struct {
	num int
	pid int
}
type hchan struct {
	qcount   uint           // total data in the queue
	dataqsiz uint           // size of the circular queue
	buf      unsafe.Pointer // points to an array of dataqsiz elements
	elemsize uint16
	closed   uint32
}

type ChanInfo struct {
	Closed bool // 是否关闭
	Len    uint // channel内数据量
	Cap    uint // channel容量
	Block  bool // 是否已经阻塞
}

func ChanStatus(c interface{}) (*ChanInfo, error) {
	v := reflect.ValueOf(c)
	if v.Type().Kind() != reflect.Chan {
		return nil, errors.New("type must be channel")
	}
	i := (*[2]uintptr)(unsafe.Pointer(&c))
	h := (*hchan)(unsafe.Pointer(i[1]))
	return &ChanInfo{
		Cap:    h.dataqsiz,
		Len:    h.qcount,
		Closed: h.closed == 1,
		Block:  h.qcount >= h.dataqsiz,
	}, nil
}
func GetCurrentThreadId() int {
	var user32 *syscall.DLL
	var GetCurrentThreadId *syscall.Proc
	var err error

	user32, err = syscall.LoadDLL("Kernel32.dll")
	if err != nil {
		fmt.Printf("syscall.LoadDLL fail: %v\n", err.Error())
		return 0
	}
	GetCurrentThreadId, err = user32.FindProc("GetCurrentThreadId")
	if err != nil {
		fmt.Printf("user32.FindProc fail: %v\n", err.Error())
		return 0
	}

	var pid uintptr
	pid, _, err = GetCurrentThreadId.Call()

	return int(pid)
}

func f1(ch1 chan int) {
	defer wg.Done()
	for i := 0; i < 98; i++ {
		ch1 <- i
	}
	ch1 <- 666
	ch1 <- 666
	close(ch1)
}

func f2(ch1 chan int, ch2 chan Str) {

	defer wg.Done()
	for {
		//ChanInfo, _ := ChanStatus(ch1)
		x, ok := <-ch1
		if x == 666 && ok == true {
			break
		}
		var s Str
		s.num = x
		s.pid = GetCurrentThreadId()
		//if ChanInfo.Closed {
		ch2 <- s
		//}
	}
}

func f3(ch1 chan int, ch2 chan Str) {

	defer wg.Done()
	for {
		x, ok := <-ch1
		if !ok {
			break
		}
		var s Str
		s.num = x
		s.pid = GetCurrentThreadId()
		ch2 <- s //可能是运行到这

	}
	// close(ch2)
	once.Do(func() { close(ch2) })
}

func main() {

	ch1 := make(chan int, 100)
	ch2 := make(chan Str, 100)
	// ch3 := make(chan Str, 100)
	wg.Add(3)

	go f1(ch1)
	go f2(ch1, ch2)
	go f2(ch1, ch2)
	wg.Wait()
	close(ch2)
	for {
		x, ok := <-ch2
		if !ok {
			break
		}
		fmt.Println(x.num, x.pid)
	}
	// 直接判断状态会有问题,就是运行的时候可能是这个函数先运行了,第一次就直接ok==false跳出了
	//于是我拿到了chan的结构信息,想通过长度或者close的状态是否关闭来判断,就又会有可能出现在同一个索引上取值,或者第一个运行到关闭那了,第二个运行到赋值那了
	//因为两个结束标记是666 ,第一个线程判断第一个666为真就跳出去执行了,但是第二个线程要么拿到第二个666然后跳出,要么跟第一个同时拿到,因为值被第一个拿了,
	//第二个就拿了个空,还继续去赋值,还在赋值的时候,第一个又还没执行到close的可能,就panic: send on closed channel
	//close要在外部关闭, 关闭后不能继续赋值

}

  



这篇关于goroutine和chan的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程