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

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

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

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
390
1
755 899
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

Это во многом зависит от библиотеки, которую вы решите использовать. Например, если вы используете библиотеку 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

Вы забыли удалить ветку. Возможно, вы хотели создать ветку отстраненный?

aib 05.11.2008 21:57

Да, верно, я извлек код из некоторого кода, над которым сейчас работаю, и, конечно же, где-то хранится ссылка на поток, чтобы впоследствии присоединиться, остановить и удалить его. Спасибо. :)

LorenzCK 06.11.2008 19:06

Что ж, технически любой такой объект будет построен на основе библиотеки потоков в стиле C, потому что C++ только что указал стандартную модель std::thread в 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;
}

Поток ускорения - это здорово - моя единственная проблема заключалась в том, что вы не могли (когда я последний раз его использовал) фактически получить доступ к собственному базовому дескриптору потока, поскольку он был частным членом класса! В win32 есть ТОННА вещей, для которых вам нужен дескриптор потока, поэтому мы изменили его, чтобы сделать дескриптор общедоступным.

Orion Edwards 05.11.2008 22:40

Другая проблема с boost :: thread заключается в том, что, насколько я помню, у вас нет свободы устанавливать размер стека нового потока - функция, которая, к сожалению, не включена в стандарт C++ 0x.

Edward KMETT 05.11.2008 23:13

Этот код дает мне исключение boost :: noncopyable для этой строки: thread thread_1 = thread (task1); Может это потому, что я использую более старую версию (1.32).

Frank 21.02.2009 03:42

task1 не был объявлен в этой области?

Jake Gaston 22.03.2019 23:45
Ответ принят как подходящий

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

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).

@ user2568508 Находясь в стандартной библиотеке, должно быть, да. Хотя вам воля необходимо приобрести компилятор с этой поддержкой.

MasterMastic 20.09.2013 16:27

@Ken: Я не уверен, что вы имеете в виду, приобретая компилятор с такой поддержкой ... У некоторых компиляторов его нет?

user2568508 20.09.2013 16:39

@ user2568508 Действительно. это функция C++ 0x (C++ 11). Я не думаю, что компилятор, поддерживающий все функции C++ 0x, еще существует, но у них есть частичная поддержка, и это общая функция для них.

MasterMastic 20.09.2013 16:51

Пожалуйста, помогите, там написано «невозможно открыть включаемый файл: 'поток': нет такого файла или каталога». Я использую VS10 на Win7.

Preza8 24.10.2013 22:22

@ Preza8 Я не пробовал этого с VS10, но можно ли добавить другие стандартные заголовки? Если да, проверьте, поддерживает ли это ваша версия MSVC++.

MasterMastic 26.10.2013 00:57

@ Preza8 VS10 не поддерживает многие функции C++ 11. VS12 и VS13 поддерживают резьбу.

jodag 09.01.2014 04:53

В комментарии «Версии GCC ниже 4.7 используйте -std=c++0x (вместо -std=c++0x)» я считаю, что второй «C++ 0x» должен быть «C++ 11», но это невозможно изменить, потому что редактирование слишком мало.

zentrunix 25.03.2014 20:19

@MasterMastic Какая разница, если вместо std :: thread t1 (task1, "Hello") мы используем std :: thread t1 (& task1, "Hello"), передавая ссылку на функцию? Должны ли мы в этом случае объявить функцию как указатель?

user2452253 04.11.2014 19:32

@ user2452253 Если вы передадите "task1", ​​вы передадите указатель на функцию, которую хотите выполнить (что хорошо). Если вы передадите «& task1», вы передадите указатель на указатель функции, и результаты, вероятно, будут не такими красивыми и очень неопределенными (это было бы похоже на попытку выполнить случайные инструкции одну за другой. С большой вероятностью вы просто мог бы открываете ворота в ад). Вы действительно просто хотите передать указатель функции (указатель со значением адреса, с которого начинается функция). Если это не проясняет ситуацию, дайте мне знать и удачи!

MasterMastic 04.11.2014 19:47

Мне все еще нужен -pthread, чтобы скомпилировать его с 4.7.1, иначе программа не сможет работать с «завершением, вызванным после выброса экземпляра 'std :: system_error' what (): Operation not allowed».

BeniBela 18.02.2015 16:32

Что мне делать, если я хочу, чтобы функция принимала поток, из которого она вызывается, в качестве параметра?

Erik W 15.03.2015 22:31

@ErikW В вызывающем потоке вы можете вызвать std::this_thread::get_id(), чтобы получить свой std::thread::id; тогда вы просто проходите его регулярно (то есть как я прошел string в ответе).

MasterMastic 16.03.2015 12:05

Я считаю, что есть опечатка, поток t1 (task1, "Привет"); должен быть поток t1 (& task1, "Привет");

Mich 07.07.2015 03:05

@MasterMastic должен быть недействительным task1 ?? У меня есть функция, которая возвращает значение. Что я должен делать??

Fatemeh Karimi 12.01.2018 17:32

@MasterMastic "вы передаете указатель на указатель функции" Какой указатель на функцию?

curiousguy 05.07.2018 11:08

@FatemehKarimi, извини, что ответил так поздно. Как правило, вы должны использовать конструкцию, отличную от потока, и я говорю конкретно о будущем / обещании. Я уверен, что вы можете найти множество реализаций, в том числе и в стандартной библиотеке. Если вам нужно более сложное решение, вы, вероятно, поищете подходящий (т. Е. Потокобезопасный) механизм или контейнер для передачи данных, например очередь.

MasterMastic 05.07.2018 12:16

@curiousguy Что ж, правильный способ сделать это - передать указатель на функцию, которую вы хотите запустить. В данном случае это функция task1. Таким образом, указатель на функцию также обозначается task1. Но спасибо, что подняли этот вопрос, потому что я считаю, что был неправ, и это эквивалентно &task1, поэтому не имеет значения, какую форму вы выберете для записи (если так, я предполагаю, что указатель на указатель функции - &&task1 - который будет Будь плохим). Соответствующий вопрос.

MasterMastic 05.07.2018 12:22

@MasterMastic Это будет and task1

curiousguy 05.07.2018 15:17

Здесь можно добавить, что если вы не хотите использовать join в потоке, вы, вероятно, захотите detach - stackoverflow.com/questions/25559918/…

gosukiwi 29.06.2020 22:33

Также существует библиотека 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

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

При поиске примера класса 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
    }
}

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

Спасибо, что разместили это. Обратите внимание, что общая форма вызова потока для метода экземпляра выглядит примерно так: Foo f; std :: thread t (& Foo :: Run, & f, args ...); (где Foo - это класс, который имеет Run () в качестве функции-члена).

Jay Elston 08.01.2019 22:44

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

hkBattousai 15.02.2020 16:00

Измените это на shared_from_this, если текущий класс является производным от enable_shared_from_this.

Eugene 01.02.2021 18:39

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

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

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

int main() {
    int localVariable = 100;

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

    th.join();

    return 0;
}

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

Другие вопросы по теме