Rušné čekání - Busy waiting

Ve výpočetní technice a softwarovém inženýrství je zaneprázdněné čekání , opakování nebo točení technikou, ve které proces opakovaně kontroluje, zda je splněna podmínka, například zda vstup z klávesnice nebo zámekje k dispozici. Spinning lze také použít ke generování libovolného časového zpoždění, což je technika, která byla nezbytná u systémů, které postrádaly způsob čekání na určitou dobu. Rychlost procesoru se u jednotlivých počítačů velmi liší, zejména proto, že některé procesory jsou navrženy tak, aby dynamicky upravovaly rychlost na základě aktuální pracovní zátěže. V důsledku toho může točení jako technika časového zpoždění produkovat nepředvídatelné nebo dokonce nekonzistentní výsledky na různých systémech, pokud není zahrnut kód pro určení času, který procesor potřebuje k provedení smyčky „nedělat nic“ , nebo pokud kód smyčky výslovně kontroluje hodiny reálného času .

Ve většině případů je točení považováno za anti-vzor a mělo by se mu vyhnout, protože čas procesoru, který by mohl být použit k provedení jiného úkolu, je místo toho zbytečný na zbytečnou aktivitu. Spinning může být za určitých okolností platnou strategií, zejména při implementaci spinlocků v operačních systémech určených pro běh na systémech SMP .

Příklad C kód

Následující příklady kódu C ilustrují dvě vlákna, která sdílejí globální celé číslo i . První vlákno používá zaneprázdněné čekání ke kontrole změny hodnoty i :

#include <pthread.h>
#include <stdatomic.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

/* i is global, so it is visible to all functions. It makes use of the special
 * type atomic_int, which allows atomic memory accesses.
 */
atomic_int i = 0;

/* f1 uses a spinlock to wait for i to change from 0. */
static void *f1(void *p)
{
    int local_i;
    /* Atomically load current value of i into local_i and check if that value
       is zero */
    while ((local_i = atomic_load(&i)) == 0) {
        /* do nothing - just keep checking over and over */
    }

    printf("i's value has changed to %d.\n", local_i);
    return NULL;
}

static void *f2(void *p)
{
    int local_i = 99;
    sleep(10);   /* sleep for 10 seconds */
    atomic_store(&i, local_i);
    printf("t2 has changed the value of i to %d.\n", local_i);
    return NULL;
}

int main()
{
    int rc;
    pthread_t t1, t2;

    rc = pthread_create(&t1, NULL, f1, NULL);
    if (rc != 0) {
        fprintf(stderr, "pthread f1 failed\n");
        return EXIT_FAILURE;
    }

    rc = pthread_create(&t2, NULL, f2, NULL);
    if (rc != 0) {
        fprintf(stderr, "pthread f2 failed\n");
        return EXIT_FAILURE;
    }

    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    puts("All pthreads finished.");
    return 0;
}

V případě použití, jako je tato, je možné zvážit použití C11 ‚s proměnné podmínky .

Alternativy

Většina operačních systémů a knihoven vláken poskytuje řadu systémových volání, která zablokují proces při události, jako je získání zámku, změny časovače, dostupnost I/O nebo signály . Používání takových hovorů obecně přináší nejjednodušší, nejefektivnější, nejspravedlivější a bez závodů výsledek. Jediné volání zkontroluje, informuje plánovače o události, na kterou čeká, vloží tam, kde je to možné, bariéru paměti a může provést požadovanou I/O operaci před návratem. Ostatní procesy mohou využívat CPU, zatímco volající je blokován. Plánovači jsou poskytnuty informace potřebné k implementaci prioritní dědičnosti nebo jiných mechanismů, aby se zabránilo hladovění .

Samotné zaneprázdněné čekání může být mnohem méně nehospodárné pomocí funkce zpoždění (např. sleep()), Kterou najdete ve většině operačních systémů. Tím vlákno uspí na zadanou dobu, během které vlákno neztrácí čas CPU. Pokud smyčka kontroluje něco jednoduchého, stráví většinu času spánkem a promrhá velmi málo času CPU.

V programech, které nikdy neskončí (například operační systémy), nekonečný zaneprázdněn čekání může být implementován pomocí nepodmíněné skoky, jak je uvedeno v této NASM syntaxe: jmp $. CPU bezpodmínečně navždy přeskočí do své vlastní polohy . Rušné čekání, jako je toto, lze nahradit:

sleep:
hlt
jmp sleep

Další informace najdete v HLT (instrukce x86) .

Vhodné použití

V programování na nízké úrovni může být rušivé čekání ve skutečnosti žádoucí. Implementace zpracování řízeného přerušením pro každé hardwarové zařízení, zejména pro ta, ke kterým se přistupuje zřídka, nemusí být žádoucí ani praktické. Někdy je nutné zapsat nějaký druh řídicích dat na hardware a poté načíst stav zařízení vyplývající z operace zápisu, stav, který nemusí být platný, dokud po zápisu neuplyne několik strojových cyklů. Programátor by mohl zavolat funkci zpoždění operačního systému, ale může to zabrat více času, než by bylo vynaloženo na několik hodinových cyklů čekání na to, až zařízení vrátí svůj stav.

Viz také

Reference

externí odkazy