Skip to content

Instantly share code, notes, and snippets.

@Dovias
Last active March 20, 2025 08:10
Show Gist options
  • Save Dovias/734f0bcf2820107eb89e9995fbd6df66 to your computer and use it in GitHub Desktop.
Save Dovias/734f0bcf2820107eb89e9995fbd6df66 to your computer and use it in GitHub Desktop.

pthreads gijos (POSIX)

Sukurti giją galime parašius šį kodą:

int pthread_create(pthread_t *, const pthread_attr_t *, void * (*)(void *), void *);
  • int - skaičius, kurį gražina funkcija atitinkamai ar gija buvo sukurta, ar ne.
  • pthread_t * (unsigned int *) - rodyklė į vietą, kur funkcija išsaugos gijos identifikatorių.
  • pthread_attr_t * - rodyklė į tą vietą, kur saugomi gijai būdingi atributai (gali būti NULL, tada bus naudojami numatyti atributai.)
  • void * (*)(void *) - rodyklė į tą vietą, kur yra saugomas funkcijos kodas.
  • void * - rodyklė į tą vietą, kur saugomas į funkciją paduodamas vienas parametras (gali būti bet kokia reikšmė) Sujungti gijas į vieną pagrindinę galima parašius kodą (C programavimo kalba)
int pthread_join(pthread_t thread, void **retval);
  • int - skaičius, kurį gražina funkcija atitinkamai ar gijos buvo sėkmingai sujungtos, ar ne.
  • pthread_t (unsigned int) - skaičius reprezentuojantis gijos identifikatorių.
  • void **retval - rodyklė į rodyklę, kuri nurodo vietą į kurią bus saugoma reikšmė, kuri buvo grąžinama sujungiant giją su esama (įrašius NULL, reikšmė ignoruojama)

Programavimo pavyzdys (C++)

class thread_data {
public:
    int x;
};

void * PrintHello(void * passedData) {
    thread_data * myData = reinterpret_cast < thread_data * > (passedData);
    cout << "Hello World! From thread " << myData -> x << endl;
    delete myData; // free the memory
    return NULL;
}

int main(int argc, char * argv[]) {
    const int NUM_THREADS = 4;
    pthread_t threads[NUM_THREADS];
    for (int t = 0; t < NUM_THREADS; t++) {
        cout << "In main: creating thread " << t << endl;
        thread_data * data = new thread_data();
        data -> x = t;
        pthread_create( & threads[t], NULL, PrintHello, data);
    }
    for (int t = 0; t < NUM_THREADS; t++)
        pthread_join(threads[t], NULL);
    return 0;
}

Barjerai

Barjerai tai yra tam tikri objektai, neleidžiantys procesoriui išdėstyti tam tikras skaitymo, rašymo, skaičiavimo funkcijas, instrukcijų konvejeryje. Tai reikalinga norint dalinai užtikrinti kad nebūtų duomenų lenktynių (angl. data race), paleidžiant keletą gijų vienu metu.

Sukurti barjerą galime parašius šį kodą (C programavimo kalba):

int pthread_barrier_init(pthread_barrier_t *restrict barrier, const pthread_barrierattr_t *restrict attr, unsigned count);
  • restrict - raktažodis C programavimo kalboje, kuris skirtas kompiliatoriui optimizuoti rodyklinius parametrus, jeigu rodyklės nesaugo adresų į vienas kitą.
  • pthread_barrier_t *restrict - rodyklė į vietą, kur funkcija išsaugos barjero identifikatorių.
  • pthread_barrierattr_t *restrict attr - rodyklė į tą vietą kurioje saugomi atitinkami barjero atributai. (gali būti NULL, tada bus naudojami numatyti atributai.)
  • unsigned count - skaičius, kuris parodo kiek gijų barjeras turi palaikyti, norint užtikrinti jo veikimą.

Pristabdyti giją iki tol kos bus atlikti gijos instrukcijų konvejerio operacijos galime parašius šį kodą (C programavimo kalba):

int pthread_barrier_wait(pthread_barrier_t *barrier);
  • pthread_barrier_t * - rodyklė į vietą, kur yra išsaugotas barjero identifikatorius.

Panaikinti barjerą iš heap atminties galima panaudojus ši kodą (C programavimo kalba):

int pthread_barrier_destroy(pthread_barrier_t *barrier);
  • pthread_barrier_t * - rodyklė į vietą, kur yra išsaugotas barjero identifikatorius.

Muteksai (angl. mutexes, locks)

Muteksai tai yra tam tikri objektai kuriuos panaudojus, gijos negali paleisti tam tikro kodo fragmento lygiagrečiai. Tai reikalinga norint dalinai užtikrinti kad nebūtų duomenų lenktynių (angl. data race), paleidžiant keletą gijų vienu metu.

Norint sukurti muteksą galima panaudoti šį kodą (C programavimo kalba):

int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr)
  • restrict - raktažodis C programavimo kalboje, kuris skirtas kompiliatoriui optimizuoti rodyklinius parametrus, jeigu rodyklės nesaugo adresų į vienas kitą.
  • pthread_mutex_t *restrict mutex - rodyklė į vietą, kur funkcija išsaugos mutekso identifikatorių.

Warning

Mutekso adreso vietą reikia inicializuoti su tam tikra PTHREAD_MUTEX_INITIALIZER, norint užtikrinti sklandų mutekso sukūrimą. Ta galima padaryti panaudojus šį kodą (C programavimo kalba):

pthread_mutex_t amutex = PTHREAD_MUTEX_INITIALIZER;

Siekiant blokuoti kitas gijas, gijos veikimo metu, mes galime užrakinti muteksą panaudojant šį kodą (C programavimo kalba):

int pthread_mutex_lock(pthread_mutex_t *mutex);
  • pthread_mutex_t *mutex - rodyklė į vietą, kur yra išsaugotas mutekso identifikatorius.

Siekiant pasibaigus tam tikram darbui nebeblokuoti kitų gijų, gijos veikimo metu, mes galime atrakinti muteksą panaudojant šį kodą (C programavimo kalba):

int pthread_mutex_unlock(pthread_mutex_t *mutex);
  • pthread_mutex_t *mutex - rodyklė į vietą, kur yra išsaugotas mutekso identifikatorius.

Warning

Muteksą užrakinti ir atrakinti reikėtų atsargiai, kadangi neteisingai panaudojus muteksą, gali iškilti vienos kitos gijų užsiblokavimo rizika.

Panaikinti muteksą iš heap atminties galima panaudojant šį kodą (C programavimo kalba):

int pthread_mutex_destroy(pthread_mutex_t *mutex);
  • pthread_mutex_t *mutex - rodyklė į vietą, kur yra išsaugotas mutekso identifikatorius.

OpenMP gijos

OpenMP yra taikomųjų programų programavimo sąsaja (API), kuri palaiko kelių platformų bendros atminties kelių procesų programavimą lengvu bei intuityviu būdų, nekeičiant esminio nuoseklaus kodo.

Kad atlikti tam tikrą kodo fragmentą lygiagrečiai, naudojant OpenMP užtenka parašyti tik vieną kodo eilutę prie esamo nuoseklaus kodo (C programavimo kalba):

#pragma omp parallel
std::cout << "Hello, World!\n";

Viršuje esantis kodas padaro tik vieną kodo eilutę veikiančią lygiagrečiai, tačiau jeigu norime paleisti tam tikrą kodo fragmentą didesnį nei vieną eilutę, mes norimą kodą galima apskliausti (C programavimo kalba):

#pragma omp parallel {
    int id = omp_get_thread_num();
    cout << "Hello, world, from thread - " << id << endl;
    if (id == 0) {
        int nthreads = omp_get_num_threads();
        cout << "Number of threads = " << nthreads << endl;
    }
}

Gijų metaduomenys

Rašyti kodą kuris tik veikia lygiagrečiai bet atlieką tapatį darbą gali atrodyti nepraktiška, todėl OpenMP suteikia galimybę identifikuoti giją, siekiant apskaičiuoti skirtingus duomenis lygiagrečiu būdu. Identifikuoti giją galima panaudojant šį kodą (C programavimo kalba):

int omp_get_thread_num();
  • int skaičius, kuris parodo koks gijos numeris šiuo metu atlieka lygiagrečius skaičiavimus.

Siekiant sužinoti kiek iš viso gijų atlieka lygiagrečius skaičiavimus tam tikram kodo fragmentui galima panaudojant šį kodą (C programavimo kalba):

int omp_get_num_threads();
  • int skaičius, kuris parodo kiek tam tikras lygiagrečiai veikiantis kodo fragmentas turi gijų.

Pavyzdys (C++ programavimo kalba)

#pragma omp parallel {
    int id = omp_get_thread_num();
    std::cout << "Hello, world, from thread - " << id << std::endl;
    if (id == 0) {
        int nthreads = omp_get_num_threads();
        std::cout << "Number of threads = " << nthreads << std::endl;
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment