西维蜀黍

【Golang】使用 - 读取文件

读取整个文件 - ioutil.ReadFile

ReadFile reads the file named by filename and returns the contents. A successful call returns err == nil, not err == EOF. Because ReadFile reads the whole file, it does not treat an EOF from Read as an error to be reported.

package main

import (
	"fmt"
	"io/ioutil"
)

func check(e error) {
	if e != nil {
		panic(e)
	}
}

func main() {
  // the most basic file reading task is slurping a file’s entire contents into memory
	dat, err := ioutil.ReadFile("test.txt")
	check(err)
	fmt.Print(string(dat))
}

Ouput:

1
2
3
4
5
6
  ...


【Golang】Concurrent Control

Concurrency

Share by communicating

Concurrent programming is a large topic and there is space only for some Go-specific highlights here.

Concurrent programming in many environments is made difficult by the subtleties required to implement correct access to shared variables. Go encourages a different approach in which shared values are passed around on channels and, in fact, never actively shared by separate threads of execution. Only one goroutine has access to the value at any given time. Data races cannot occur, by design. To encourage this way of thinking we have reduced it to a slogan:

Do not communicate by sharing memory; instead, share memory by communicating.

This approach can be taken too far. Reference counts may be best done by putting a mutex around an integer variable, for instance. But as a high-level approach, using channels to control access makes it easier to write clear, correct programs.

One way to think about this model is to consider a typical single-threaded program running on one CPU. It has no need for synchronization primitives. Now run another such instance; it too needs no synchronization. Now let those two communicate; if the communication is the synchronizer, there’s still no need for other synchronization. Unix pipelines, for example, fit this model perfectly. Although Go’s approach to concurrency originates in Hoare’s Communicating Sequential Processes (CSP), it can also be seen as a type-safe generalization of Unix pipes.

  ...


【Golang】Embedding

Embedding Types Within A Struct

A field declared with a type but no explicit field name is called an embedded field.

// Golang program to illustrate the
// concept of inheritance
package main

import (
	"fmt"
)

type Base struct {
	b int
}

func (base *Base) Describe() string {
	return fmt.Sprintf("base %d belongs to us", base.b)
}

type Container struct { // Container is the embedding struct
	Base // Base is the embedded field
	c    string
}

func main() {
	co := Container{}
	co.b = 1
	co.c = "string"

	// Read the fields within embedded fields
	fmt.Printf("co.b: %v - %v\n", co.b, &co.b)
	fmt.Printf("co.Base.b: %v - %v \n", co.Base.b, &co.Base.b)

	// Call the methods of embedded fields
	fmt.Println(co.Describe())
	fmt.Println(co.Base.Describe())
}
  ...


【Golang】源码 - sync - RWMutex

Source Code

A RWMutex is a reader/writer mutual exclusion lock. The lock can be held by an arbitrary number of readers or a single writer. The zero value for a RWMutex is an unlocked mutex.

A RWMutex must not be copied after first use.

If a goroutine holds a RWMutex for reading and another goroutine might call Lock, no goroutine should expect to be able to acquire a read lock until the initial read lock is released. In particular, this prohibits recursive read locking. This is to ensure that the lock eventually becomes available; a blocked Lock call excludes new readers from acquiring the lock.

type RWMutex struct {
	w           Mutex  // held if there are pending writers
	writerSem   uint32 // semaphore for writers to wait for completing readers
  // 这个 semaphore 在作用是:当ongoing reader 完成了,waiting writer可以被通知到,并且解除blocked,以进行writing
  // 调用 runtime_Semrelease(&rw.writerSem) 可以unblock blocked writer (by wakeup it via semaphore),这时候调用了 runtime_SemacquireMutex(&rw.writerSem) 的地方就不会继续被blocked了
  // 调用 runtime_SemacquireMutex(&rw.writerSem) 的地方会被 blocked,直到 runtime_Semrelease(&rw.writerSem) 被调用
  
	readerSem   uint32 // semaphore for readers to wait for completing writers
  // 这个 semaphore 在作用是:当ongoing writing 完成了,waiting reader可以被通知到,并且解除blocked,以进行reading
  // 调用 runtime_Semrelease(&rw.readerSem) 可以 Unblock blocked readers (by wakeup them via semaphore),这时候调用了 runtime_SemacquireMutex(&rw.readerSem) 的地方就不会继续被blocked了
  // 调用 runtime_SemacquireMutex(&rw.readerSem) 的地方会被 block,直到 runtime_Semrelease(&rw.readerSem) 被调用
	readerCount int32  // number of pending readers
  // if readerCount < 0, means a writing is ongoing (thus all readers have to wait, until the writing is done)
  // if readerCount == 0, means no ongoing writing and no onging reading
  // if readerCount > 0, mean has onging reading, but no ongoing writing
  
	readerWait  int32  // number of departing readers
}

这意味着:

  • 当有writer进来时,
    • 如果这时候没有ongoing reader,writer就直接write
    • 如果这时候有ongoing reader,writer就一直等待(被阻塞在runtime_SemacquireMutex(&rw.writerSem),直到
  • 当有reader进来时,
    • 如果这时候没有ongoing writer,reader就直接read
    • 如果这时候有ongoing writer,reader一直等待(被阻塞在untime_SemacquireMutex(&rw.readerSem)),直到writer完成(writer完成后,会调用runtime_Semrelease(&rw.readerSem)来通知所有reader)
  ...


【Golang】源码 - Channel 实现

Channel 在运行时的内部表示是 runtime.hchan,该结构体中包含了用于保护成员变量的互斥锁,从某种程度上说,Channel 是一个用于同步和通信的有锁队列,使用互斥锁解决程序中可能存在的线程竞争问题是很常见的,我们能很容易地实现有锁队列。

  ...