Простой пример многопоточности в C ++

Может ли кто-нибудь опубликовать простой пример запуска двух (объектно-ориентированных) потоков на C ++.

Я ищу реальные объекты потоков C ++, которые я могу расширить для методов запуска (или что-то подобное), а не вызывать библиотеку потоков в стиле C.

Я пропустил любые запросы, специфичные для ОС, в надежде, что тот, кто ответит, ответит с использованием кроссплатформенных библиотек. Я сейчас просто разъясняю это.


person Zak    schedule 05.11.2008    source источник
comment
Этот вопрос обсуждается на мета   -  person Zoe    schedule 13.08.2019


Ответы (6)


Создайте функцию, которую вы хотите, чтобы поток выполнялся, например:

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

Теперь создайте объект thread, который в конечном итоге вызовет указанную выше функцию следующим образом:

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

(Вам необходимо #include <thread>, чтобы получить доступ к классу std::thread)

Аргументы конструктора - это функция, которую будет выполнять поток, за которой следуют параметры функции. Поток запускается автоматически при построении.

Если позже вы захотите дождаться, пока поток завершит выполнение функции, вызовите:

t1.join(); 

(Присоединение означает, что поток, который вызвал новый поток, будет ждать, пока новый поток завершит выполнение, прежде чем он продолжит собственное выполнение).


Код

#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();
}

Подробнее о std :: thread здесь

  • В GCC компилируйте с -std=c++0x -pthread.
  • Это должно работать для любой операционной системы, если ваш компилятор поддерживает эту функцию (C ++ 11).
person MasterMastic    schedule 27.06.2012
comment
@ user2568508 Находясь в стандартной библиотеке, должно быть, да. Хотя вам потребуется приобрести компилятор с этой поддержкой. - person MasterMastic; 20.09.2013
comment
@Ken: Я не уверен, что вы имеете в виду, приобретая компилятор с такой поддержкой ... У некоторых компиляторов его нет? - person ; 20.09.2013
comment
@ user2568508 Действительно. это функция C ++ 0x (C ++ 11). Я не думаю, что компилятор, поддерживающий все функции C ++ 0x, еще существует, но у них есть частичная поддержка, и это общая функция для них. - person MasterMastic; 20.09.2013
comment
Пожалуйста, помогите, он говорит, что невозможно открыть включаемый файл: 'thread': нет такого файла или каталога. Я использую VS10 на Win7. - person Preza8; 24.10.2013
comment
@ Preza8 Я не пробовал этого с VS10, но можно ли добавить другие стандартные заголовки? Если да, проверьте, поддерживает ли это ваша версия MSVC ++. - person MasterMastic; 26.10.2013
comment
@ Preza8 VS10 не поддерживает многие функции C ++ 11. VS12 и VS13 поддерживают резьбу. - person jodag; 09.01.2014
comment
В комментариях к версиям GCC ниже 4.7 используйте -std=c++0x (вместо -std=c++0x). Я считаю, что второй c ++ 0x должен быть c ++ 11, но это невозможно изменить, потому что редактирование слишком мало. - person zentrunix; 25.03.2014
comment
@MasterMastic Какая разница, если вместо std :: thread t1 (task1, Hello) мы будем использовать std :: thread t1 (& task1, Hello), передав ссылку на функцию? Должны ли мы в этом случае объявить функцию как указатель? - person user2452253; 04.11.2014
comment
@ user2452253 Если вы передаете task1, вы передаете указатель на функцию, которую хотите выполнить (что хорошо). Если вы передадите & task1, вы передадите указатель на указатель функции, и результаты, вероятно, будут не такими красивыми и очень неопределенными (это было бы похоже на попытку выполнить случайные инструкции одну за другой. С правильной вероятностью вы просто может открыть врата в ад). Вы действительно просто хотите передать указатель функции (указатель со значением адреса, с которого начинается функция). Если это не проясняет ситуацию, дайте мне знать и удачи! - person MasterMastic; 04.11.2014
comment
Мне все еще нужен -pthread, чтобы скомпилировать его с 4.7.1, иначе программа не сможет работать с terminate, вызванным после выброса экземпляра 'std :: system_error' what (): Operation not allowed. - person BeniBela; 18.02.2015
comment
Что делать, если я хочу, чтобы функция принимала поток, из которого она вызывается, в качестве параметра? - person Erik W; 15.03.2015
comment
@ErikW В вызывающем потоке вы можете вызвать std::this_thread::get_id(), чтобы получить свой _ 2_; тогда вы просто передаете его регулярно (т.е. когда я передал string в ответе). - person MasterMastic; 16.03.2015
comment
Я считаю, что есть опечатка, поток t1 (task1, Hello); должен быть поток t1 (& task1, Hello); - person Mich; 07.07.2015
comment
@MasterMastic должен быть недействительным task1 ?? У меня есть функция, которая возвращает значение. что я должен делать?? - person Fatemeh Karimi; 12.01.2018
comment
@MasterMastic вы передаете указатель на указатель функции Какой указатель на функцию? - person curiousguy; 05.07.2018
comment
@FatemehKarimi, извини, что ответил так поздно. Как правило, вы должны использовать конструкцию, отличную от потока, и я говорю конкретно о будущем / обещании. Я уверен, что вы можете найти множество реализаций, в том числе и в стандартной библиотеке. Если вам нужно больше возможностей, вы, вероятно, поищете подходящий (т.е. поточно-ориентированный) механизм или контейнер для передачи данных, например очередь. - person MasterMastic; 05.07.2018
comment
@curiousguy Что ж, правильный способ сделать это - передать указатель на функцию, которую вы хотите запустить. В этом случае функция task1. Таким образом, указатель на функцию также обозначается task1. Но спасибо, что подняли этот вопрос, потому что я считаю, что был неправ, и это эквивалентно &task1, поэтому не имеет значения, какую форму вы выберете для записи (если это так, я предполагаю, что указатель на указатель функции &&task1 - это было бы плохо). Соответствующий вопрос. - person MasterMastic; 05.07.2018
comment
@MasterMastic Это было бы and task1 - person curiousguy; 05.07.2018
comment
Здесь следует добавить следующее: если вы не хотите join поток, вы, вероятно, захотите detach - stackoverflow.com/questions/25559918/ - person gosukiwi; 29.06.2020

Что ж, технически любой такой объект будет построен на основе библиотеки потоков в стиле C, потому что в C ++ только что указан stock _ 1_ в c ++ 0x, которая была только что прибита и еще не реализована. Проблема носит в некоторой степени системный характер, технически существующая модель памяти C ++ недостаточно строгая, чтобы обеспечить четко определенную семантику для всех случаев, когда «произошло раньше». Некоторое время назад Ханс Бём написал статью по этой теме и сыграл важную роль в разработке стандарта C ++ 0x по этой теме.

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

Тем не менее, существует несколько библиотек C ++ для кросс-платформенных потоков, которые на практике прекрасно работают. Строительные блоки Intel thread содержат объект tbb :: thread, который близко приближается к стандарту c ++ 0x, а Boost имеет библиотеку boost :: thread, которая делает то же самое.

http://www.threadingbuildingblocks.org/

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

Используя boost :: thread, вы получите что-то вроде:

#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;
}
person Edward KMETT    schedule 05.11.2008
comment
Поток Boost великолепен - моя единственная проблема заключалась в том, что вы не могли (когда я последний раз его использовал) фактически получить доступ к собственному базовому дескриптору потока, поскольку он был частным членом класса! В win32 есть ТОННА вещей, для которых вам нужен дескриптор потока, поэтому мы изменили его, чтобы сделать дескриптор общедоступным. - person Orion Edwards; 05.11.2008
comment
Другая проблема с boost :: thread заключается в том, что, насколько я помню, у вас нет свободы устанавливать размер стека нового потока - функция, которая, к сожалению, не включена в стандарт c ++ 0x. - person Edward KMETT; 05.11.2008
comment
Этот код дает мне исключение boost :: noncopyable для этой строки: thread thread_1 = thread (task1); Может это потому, что я использую более старую версию (1.32). - person Frank; 21.02.2009
comment
task1 не был объявлен в этой области? - person Jake Gaston; 22.03.2019

Также существует библиотека POSIX для операционных систем POSIX. Проверить совместимость

#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;

}

скомпилировать с -lpthread

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

person Hohenheimsenberg    schedule 18.04.2013

При поиске примера класса C ++, который вызывает один из своих собственных методов экземпляра в новом потоке, возникает этот вопрос, но мы не смогли использовать ни один из этих ответов таким образом. Вот пример, который это делает:

Class.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
    }
}

Обратите внимание, что в этом примере не рассматривается мьютекс или блокировка.

person livingtech    schedule 18.09.2017
comment
Спасибо, что разместили это. Обратите внимание, что общая форма вызова потока для метода экземпляра выглядит примерно так: Foo f; std :: thread t (& Foo :: Run, & f, args ...); (где Foo - это класс, который имеет Run () в качестве функции-члена). - person Jay Elston; 08.01.2019
comment
Спасибо, что разместили это. Я серьезно не понимаю, почему во всех примерах потоковой передачи используются глобальные функции. - person hkBattousai; 15.02.2020
comment
Измените это на shared_from_this, если текущий класс является производным от enable_shared_from_this. - person Eugene; 01.02.2021

Если не нужна отдельная функция в глобальных пространствах имен, мы можем использовать лямбда-функции для создания потоков.

Одним из основных преимуществ создания потока с использованием лямбда является то, что нам не нужно передавать локальные параметры в качестве списка аргументов. Мы можем использовать список захвата для того же самого, и свойство закрытия лямбда позаботится о жизненном цикле.

Вот пример кода

int main() {
    int localVariable = 100;

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

    th.join();

    return 0;
}

Безусловно, я обнаружил, что лямбда-выражения C ++ - лучший способ создания потоков, особенно для более простых потоковых функций.

person Deepak K Gupta    schedule 08.11.2017

Это во многом зависит от библиотеки, которую вы решите использовать. Например, если вы используете библиотеку wxWidgets, создание потока будет выглядеть так:

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;
}

Если ваш основной поток вызывает метод CreateThread, вы создадите новый поток, который начнет выполнение кода в вашем методе «Entry». В большинстве случаев вам нужно будет сохранить ссылку на поток, чтобы присоединиться к нему или остановить его. Дополнительная информация здесь: документация wxThread

person LorenzCK    schedule 05.11.2008
comment
Вы забыли удалить ветку. Возможно, вы хотели создать отдельный поток? - person aib; 05.11.2008
comment
Да, верно, я извлек код из некоторого кода, над которым сейчас работаю, и, конечно, ссылка на поток где-то хранится для того, чтобы присоединиться, остановить и удалить его позже. Спасибо. :) - person LorenzCK; 06.11.2008