Model thread tradisional (contohnya, yang biasanya digunakan saat menulis program Java, C++, Python) kadang mengharuskan pemrogram untuk berkomunikasi antar thread menggunakan memori yang saling dibagi. Biasanya, dalam bentuk struktur data yang dilindungi oleh semacam penguncian (lock), dan setiap thread tersebut akan bersaing menggunakan kunci tersebut untuk mengakses data. Pada kasus tertentu, hal ini dimudahkan dengan penggunaan struktur data yang paham tentang thread, seperti Queue pada Python.

Konkurensi primitif pada Go —goroutine dan channel— menyediakan sebuah solusi yang berbeda dan elegan untuk menulis perangkat lunak konkuren. (Konsep ini memiliki sejarah yang menarik yang dimulai dari tulisan C. A. R. Hoare tentang Communicating Sequential Processes.) Alih-alih secara eksplisit menggunakan kunci untuk menengahi akses terhadap data yang dibagi, Go mendorong penggunaan channel untuk mengirim referensi data antara goroutine. Pendekatan ini memastikan hanya satu goroutine yang memiliki akses terhadap data dalam satu waktu. Konsep ini disimpulkan dalam dokumen Efektif Go (yang harus dibaca oleh pemrogram Go).

Jangan berkomunikasi dengan berbagi memori; tapi, bagilah memori untuk berkomunikasi.

Perhatikan contoh program berikut yang memproses daftar URL. Dalam lingkungan model pemrograman thread tradisional, seseorang biasanya menulis struktur data seperti berikut:

type Resource struct {
	url        string
	polling    bool
	lastPolled int64
}

type Resources struct {
	data []*Resource
	lock *sync.Mutex
}

Dan kemudian sebuah fungsi Poller (yang berjalan di thread yang terpisah) bentuknya kurang lebih seperti berikut,

func Poller(res *Resources) {
	for {
		// ambil Resource yang terakhir dan tandai telah
		// diproses.
		res.lock.Lock()
		var r *Resource
		for _, v := range res.data {
			if v.polling {
				continue
			}
			if r == nil || v.lastPolled < r.lastPolled {
				r = v
			}
		}
		if r != nil {
			r.polling = true
		}
		res.lock.Unlock()
		if r == nil {
			continue
		}

		// proses URL...

		// perbarui Resource polling dan lastPolled.
		res.lock.Lock()
		r.polling = false
		r.lastPolled = time.Nanoseconds()
		res.lock.Unlock()
	}
}

Fungsi ini hampir sehalaman panjangnya, dan membutuhkan lebih banyak detil lagi supaya selesai. Ia bahkan tidak mengikutkan logika untuk memproses URL (yang seharusnya cukup beberapa baris saja), dan bahkan tidak juga menangani kapan pengulangan berhenti.

Mari kita lihat fungsionalitas yang sama diimplementasikan dengan idiom Go. Pada contoh ini, Poller adalah sebuah fungsi yang menerima Resource yang akan diproses dari sebuah channel masukan, dan mengirimnya ke sebuah channel keluaran setelah selesai.

type Resource string

func Poller(in, out chan *Resource) {
	for r := range in {
		// proses URL ...

		// kirim Resource yang telah diproses ke out.
		out <- r
	}
}

Logika yang kompleks dari contoh sebelumnya sudah hilang, dan struktur data Resource kita sekarang tidak ada lagi mengurus penguncian data. Malah, yang masih kurang adalah bagian yang paling penting, pemrosesan. Hal ini seharusnya memberikan Anda sebuah intuisi terhadap kekuatan dari fitur bahasa yang sederhana.

Ada banyak yang kurang dari potongan kode di atas. Untuk langkah-langkah yang komplit, program Go yang idiomatis yang menggunakan gagasan tersebut, lihat lah Berbagi memori dengan berkomunikasi.