Concurrency 101

07.11.2013

Що е то concurrency?

Concurrency vs. Parallelism

Moore's law

А какво става, когато имаме много ядра?

IO-bound vs. CPU-bound

Processes vs. Threads (Green & Native)

Подходи

В C ползват вилици

#include <stdio.h>

int main()
{
    printf("before\n");
    if (fork())
        printf("father\n");
    else
        printf("son\n");
    printf("both\n");
}

Синхронизация на вилици

#include <stdio.h>
#include <unistd.h>

int main()
{
    pid_t pid = fork();
    if (pid == 0)
    {
        execl("/bin/sleep", "/bin/sleep", "2", (char *) 0);
    }
    else
    {
        waitpid(pid, NULL, 0);
    }
    printf("done!\n");
    return 0;
}

Как работи 'ps aux | grep myprocess'?

Demo pipes.c

Предимства и недостатъци на fork

Против:

За:

В Go се правим на модерни

Нишки

Goroutines

Go е един от тези езици.

go doAnotherThing()

Пример

package main

import (
    "fmt"
    "time"
)

func main() {
    fmt.Println("Wait for my announcement...")
    Announce("Hello, from Goroutine!", time.Second * 2)
    time.Sleep(time.Second * 3)
}

func Announce(message string, delay time.Duration) {
    go func() {
        time.Sleep(delay)
        fmt.Println(message)
    }()
}

Проблеми, свързани с нишки

От това, че имат една и съща памет, следва, че могат да достъпват едни и същи променливи

int i = 0

thread1 { i++ }
thread2 { i++ }

wait { thread1 } { thread2 }
print i

Тук i накрая може да бъде 1 или 2.

Критични секции

В Go имаме Semaphors и Message passing

sync

Пакет, който ни дава синхронизационни примитиви от ниско ниво:

WaitGroup

Изчаква колекция от горутини да приключат и чак тогава продължава с изпълнението.
Така не правим простотии със time.Sleep, както одеве.

package sync

type WaitGroup struct {}

func (*WaitGroup) Add()
func (*WaitGroup) Done()
func (*WaitGroup) Wait()

Пример

package main

import (
	"fmt"
	"net/http"
	"sync"
)

func main() {
    var wg sync.WaitGroup
    var urls = []string{
        "http://www.golang.org/",
        "http://www.google.com/",
        "http://www.somestupidname.com/",
    }
    for _, url := range urls {
        // Increment the WaitGroup counter.
        wg.Add(1)
        // Launch a goroutine to fetch the URL.
        go func(url string) {
            // Decrement the counter when the goroutine completes.
            defer wg.Done()
            // Fetch the URL.
            content, err := http.Get(url)
            if err == nil {
                fmt.Println(url, content.Status)
            } else {
                fmt.Println(url, "has failed")
            }
        }(url)
    }
    // Wait for all HTTP fetches to complete.
    wg.Wait()
}

Mutex

package sync

type Mutex struct {}

func (*Mutex) Lock()
func (*Mutex) Unlock()

Once

Обект от този тип ще изпълни точно една функция.

package main

import (
	"fmt"
	"sync"
)

func main() {
    var once sync.Once
    var wg sync.WaitGroup

    onceBody := func() {
        fmt.Println("Only once")
    }
    anotherBody := func() {
        fmt.Println("Another")
    }

    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            once.Do(onceBody)
            once.Do(anotherBody)
        }()
    }
    wg.Wait()
}

Въпроси?