Prosty przykład wątkowania w C ++

391

Czy ktoś może napisać prosty przykład uruchomienia dwóch wątków (obiektowych) w C ++.

Szukam rzeczywistych obiektów wątku C ++, w których można rozszerzyć metody uruchamiania (lub czegoś podobnego), zamiast wywoływać bibliotekę wątków w stylu C.

Pominąłem wszelkie specyficzne dla systemu operacyjnego żądania w nadziei, że ktokolwiek odpowie, skorzysta z bibliotek między platformami. Po prostu to teraz wyrażam.

Zak
źródło

Odpowiedzi:

560

Utwórz funkcję, która ma być wykonywana przez wątek, np .:

void task1(std::string msg)
{
    std::cout << "task1 says: " << msg;
}

Teraz stwórz threadobiekt, który ostatecznie wywoła powyższą funkcję w następujący sposób:

std::thread t1(task1, "Hello");

(Musisz #include <thread>uzyskać dostęp do std::threadzajęć)

Argumenty konstruktora to funkcja, którą wykona wątek, a następnie parametry funkcji. Wątek jest automatycznie uruchamiany podczas budowy.

Jeśli później chcesz poczekać na zakończenie wątku w celu wykonania funkcji, wywołaj:

t1.join(); 

(Dołączenie oznacza, że ​​wątek, który wywołał nowy wątek, będzie czekał na zakończenie nowego wątku, zanim będzie kontynuował wykonywanie).


Kod

#include <string>
#include <iostream>
#include <thread>

using namespace std;

// The function we want to execute on the new thread.
void task1(string msg)
{
    cout << "task1 says: " << msg;
}

int main()
{
    // Constructs the new thread and runs it. Does not block execution.
    thread t1(task1, "Hello");

    // Do other things...

    // Makes the main thread wait for the new thread to finish execution, therefore blocks its own execution.
    t1.join();
}

Więcej informacji o std :: thread tutaj

  • Na GCC skompiluj z -std=c++0x -pthread.
  • Powinno to działać dla każdego systemu operacyjnego, pod warunkiem, że Twój kompilator obsługuje tę funkcję (C ++ 11).
MasterMastic
źródło
4
@ Preza8 VS10 nie obsługuje wielu funkcji w C ++ 11. Wątek wsparcia VS12 i VS13.
jodag
3
W komentarzu „Wersje GCC poniżej 4.7 używają” -std=c++0x(zamiast -std=c++0x) „Uważam, że drugim„ c ++ 0x ”powinno być„ c ++ 11 ”, ale nie można tego zmienić, ponieważ edycja jest zbyt mała.
zentrunix
1
@MasterMastic Co to za różnica, jeśli zamiast std :: thread t1 (task1, „Hello”) używamy std :: thread t1 (& task1, „Hello”), przekazując odwołanie do funkcji? Czy w tym przypadku powinniśmy zadeklarować funkcję jako wskaźnik?
user2452253
3
@ user2452253 Jeśli przekazujesz „task1”, przekazujesz wskaźnik do funkcji, którą chcesz wykonać (co jest dobre). Jeśli przekazujesz „& task1”, przekazujesz wskaźnik do wskaźnika funkcji, a wyniki prawdopodobnie nie byłyby tak ładne i bardzo nieokreślone (byłoby to jak próba wykonania losowych instrukcji jedna po drugiej. Z odpowiednim prawdopodobieństwem możesz po prostu otwórz bramę do piekła). Naprawdę chcesz po prostu przekazać wskaźnik funkcji (wskaźnik o wartości adresu, od którego funkcja się zaczyna). Jeśli to nie rozwiąże problemu, daj mi znać i powodzenia!
MasterMastic
2
@curiousguy Cóż, właściwym sposobem na to jest przekazanie wskaźnika funkcji, którą chcesz uruchomić. W tym przypadku funkcją jest task1. Wskaźnik funkcji jest również oznaczony przez task1. Ale dziękuję, że o tym wspomniałeś, ponieważ uważam, że się myliłem i jest to równoważne &task1, więc nie ma znaczenia, którą formę wybierzesz do pisania (jeśli tak, to myślę, że wskaźnik do wskaźnika funkcji jest &&task1- to byłoby złe ). Odpowiednie pytanie .
MasterMastic,
80

No cóż, technicznie każdy taki obiekt zostanie skończony z biblioteką wątków w stylu C, ponieważ C ++ tylko określił std::threadmodel podstawowy w c ++ 0x, który został właśnie przybity i jeszcze nie został zaimplementowany. Problem jest dość systemowy, technicznie istniejący model pamięci c ++ nie jest wystarczająco ścisły, aby umożliwić dobrze zdefiniowaną semantykę dla wszystkich przypadków „zdarzało się przed”. Hans Boehm napisał jakiś artykuł na ten temat i przyczynił się do opracowania standardu c ++ 0x na ten temat.

http://www.hpl.hp.com/techreports/2004/HPL-2004-209.html

To powiedziawszy, istnieje kilka wieloplatformowych bibliotek wątków C ++, które działają dobrze w praktyce. Bloki konstrukcyjne wątków Intel zawierają obiekt tbb :: thread, który jest zbliżony do standardu c ++ 0x, a funkcja Boost ma bibliotekę boost :: thread, która robi to samo.

http://www.threadingbuildingblocks.org/

http://www.boost.org/doc/libs/1_37_0/doc/html/thread.html

Używając boost :: thread otrzymujesz coś takiego:

#include <boost/thread.hpp>

void task1() { 
    // do stuff
}

void task2() { 
    // do stuff
}

int main (int argc, char ** argv) {
    using namespace boost; 
    thread thread_1 = thread(task1);
    thread thread_2 = thread(task2);

    // do other stuff
    thread_2.join();
    thread_1.join();
    return 0;
}
Edward KMETT
źródło
8
Zwiększenie wątku jest świetne - moim jedynym problemem było to, że nie mogłeś (kiedy go ostatnio użyłem) faktycznie uzyskać dostępu do natywnego uchwytu wątku, ponieważ był on członkiem klasy prywatnej! W Win32 jest mnóstwo rzeczy, do których potrzebujesz uchwytu wątku, więc poprawiliśmy go, aby upublicznić uchwyt.
Orion Edwards,
4
Innym problemem związanym z boost :: thread jest to, że, jak pamiętam, nie masz swobody ustawiania rozmiaru stosu nowego wątku - funkcja, która również nie jest w znacznym stopniu zawarta w standardzie c ++ 0x.
Edward KMETT,
Ten kod daje mi wyjątek :: boost, którego nie można kopiować dla tej linii: thread thread_1 = thread (task1); Może dlatego, że używam starszej wersji (1.32).
Frank
Zadanie 1 nie zostało zadeklarowane w tym zakresie?
Jake Gaston
20

Istnieje również biblioteka POSIX dla systemów operacyjnych POSIX. Sprawdź zgodność

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <iostream>

void *task(void *argument){
      char* msg;
      msg = (char*)argument;
      std::cout<<msg<<std::endl;
}

int main(){
    pthread_t thread1, thread2;
    int i1,i2;
    i1 = pthread_create( &thread1, NULL, task, (void*) "thread 1");
    i2 = pthread_create( &thread2, NULL, task, (void*) "thread 2");

    pthread_join(thread1,NULL);
    pthread_join(thread2,NULL);
    return 0;

}

skompiluj z -lpthread

http://en.wikipedia.org/wiki/POSIX_Threads

Hohenheimsenberg
źródło
18
#include <thread>
#include <iostream>
#include <vector>
using namespace std;

void doSomething(int id) {
    cout << id << "\n";
}

/**
 * Spawns n threads
 */
void spawnThreads(int n)
{
    std::vector<thread> threads(n);
    // spawn n threads:
    for (int i = 0; i < n; i++) {
        threads[i] = thread(doSomething, i + 1);
    }

    for (auto& th : threads) {
        th.join();
    }
}

int main()
{
    spawnThreads(10);
}
Caner
źródło
13

Podczas wyszukiwania przykładu klasy C ++, która wywołuje jedną z własnych metod instancji w nowym wątku, pojawia się to pytanie, ale nie byliśmy w stanie użyć żadnej z tych odpowiedzi w ten sposób. Oto przykład, który to robi:

Klasa. H

class DataManager
{
public:
    bool hasData;
    void getData();
    bool dataAvailable();
};

Class.cpp

#include "DataManager.h"

void DataManager::getData()
{
    // perform background data munging
    hasData = true;
    // be sure to notify on the main thread
}

bool DataManager::dataAvailable()
{
    if (hasData)
    {
        return true;
    }
    else
    {
        std::thread t(&DataManager::getData, this);
        t.detach(); // as opposed to .join, which runs on the current thread
    }
}

Zauważ, że ten przykład nie dotyczy muteksu ani blokowania.

livingtech
źródło
2
Dzięki za opublikowanie tego. Zauważ, że ogólna forma wywoływania metody wątku na instancji wygląda mniej więcej tak: Foo f; std :: thread t (& Foo :: Run, & f, args ...); (gdzie Foo jest klasą, która ma funkcję „Run ()” jako funkcję członka).
Jay Elston
Dzięki za opublikowanie tego. Naprawdę nie rozumiem, dlaczego wszystkie przykłady wątków używają funkcji globalnych.
hkBattousai
9

O ile nie chcemy osobnej funkcji w globalnych przestrzeniach nazw, możemy używać funkcji lambda do tworzenia wątków.

Jedną z głównych zalet tworzenia wątków przy użyciu lambda jest to, że nie musimy przekazywać parametrów lokalnych jako listy argumentów. Do tego samego możemy użyć listy przechwytywania, a właściwość zamknięcia lambda zajmie się cyklem życia.

Oto przykładowy kod

int main() {
    int localVariable = 100;

    thread th { [=](){
        cout<<"The Value of local variable => "<<localVariable<<endl;
    }}

    th.join();

    return 0;
}

Do tej pory uważam, że lambda C ++ są najlepszym sposobem tworzenia wątków, szczególnie dla prostszych funkcji wątków

Daksh
źródło
8

W dużej mierze zależy to od biblioteki, z której zdecydujesz się korzystać. Na przykład, jeśli używasz biblioteki wxWidgets, tworzenie wątku wyglądałoby tak:

class RThread : public wxThread {

public:
    RThread()
        : wxThread(wxTHREAD_JOINABLE){
    }
private:
    RThread(const RThread &copy);

public:
    void *Entry(void){
        //Do...

        return 0;
    }

};

wxThread *CreateThread() {
    //Create thread
    wxThread *_hThread = new RThread();

    //Start thread
    _hThread->Create();
    _hThread->Run();

    return _hThread;
}

Jeśli główny wątek wywołuje metodę CreateThread, utworzysz nowy wątek, który rozpocznie wykonywanie kodu w metodzie „Entry”. W większości przypadków będziesz musiał zachować odniesienie do wątku, aby go dołączyć lub zatrzymać. Więcej informacji tutaj: dokumentacja wxThread

LorenzCK
źródło
1
Zapomniałeś usunąć wątek. Być może chciałeś stworzyć oddzielny wątek?
aib
1
Tak, wyodrębniłem kod z jakiegoś kodu, nad którym obecnie pracuję i oczywiście odniesienie do wątku jest gdzieś przechowywane, aby później dołączyć, zatrzymać i usunąć. Dzięki. :)
LorenzCK