Обикновено C++ Threading

Опитвам се да създам нишка в C++ (Win32), за да стартирам прост метод. Нов съм в нишките на C++, но съм много запознат с нишките в C#. Ето някакъв псевдокод на това, което се опитвам да направя:

static void MyMethod(int data)
{
    RunStuff(data);
}

void RunStuff(int data)
{
    //long running operation here
}

Искам да извикам RunStuff от MyMethod, без той да блокира. Какъв би бил най-лесният начин за стартиране на RunStuff в отделна нишка?

Редактиране: Трябва също да спомена, че искам да сведа зависимостите до минимум. (Без MFC... и т.н.)


person Jon Tackabury    schedule 27.02.2009    source източник
comment
Обикновено c++ Threading. Някъде има виц.   -  person jason saldo    schedule 27.02.2009
comment
LOL - Знам, искаме невъзможното. Под просто имах предвид, че не споделям никакви данни между нишките и нямам нужда от заключване. Просто искам да започна темата и да забравя за нея.   -  person Jon Tackabury    schedule 27.02.2009


Отговори (10)


C++11, наличен с по-нови компилатори като Visual Studio 2013, има нишки като част от езика заедно с доста други хубави части и парчета като ламбда.

Включващият файл threads предоставя класа на нишката, който е набор от шаблони. Функционалността на нишката е в пространството от имена std::. Някои функции за синхронизиране на нишки използват std::this_thread като пространство от имена (вижте Защо std::this_thread пространство от имена? за малко обяснение).

Следният пример за конзолно приложение, използващо Visual Studio 2013, демонстрира част от функционалността на нишката на C++11, включително използването на ламбда (вижте Какво е ламбда израз в C++11?). Забележете, че функциите, използвани за заспиване на нишка, като std::this_thread::sleep_for(), използват продължителност от std::chrono.

// threading.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <chrono>
#include <thread>
#include <mutex>

int funThread(const char *pName, const int nTimes, std::mutex *myMutex)
{
    // loop the specified number of times each time waiting a second.
    // we are using this mutex, which is shared by the threads to
    // synchronize and allow only one thread at a time to to output.
    for (int i = 0; i < nTimes; i++) {
        myMutex->lock();
        std::cout << "thread " << pName << " i = " << i << std::endl;
        // delay this thread that is running for a second.  
        // the this_thread construct allows us access to several different
        // functions such as sleep_for() and yield().  we do the sleep
        // before doing the unlock() to demo how the lock/unlock works.
        std::this_thread::sleep_for(std::chrono::seconds(1));
        myMutex->unlock();
        std::this_thread::yield();
    }

    return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
    // create a mutex which we are going to use to synchronize output
    // between the two threads.
    std::mutex  myMutex;

    // create and start two threads each with a different name and a
    // different number of iterations. we provide the mutex we are using
    // to synchronize the two threads.
    std::thread myThread1(funThread, "one", 5, &myMutex);
    std::thread myThread2(funThread, "two", 15, &myMutex);

    // wait for our two threads to finish.
    myThread1.join();
    myThread2.join();

    auto  fun = [](int x) {for (int i = 0; i < x; i++) { std::cout << "lambda thread " << i << std::endl; std::this_thread::sleep_for(std::chrono::seconds(1)); } };

    // create a thread from the lambda above requesting three iterations.
    std::thread  xThread(fun, 3);
    xThread.join();
    return 0;
}
person Richard Chambers    schedule 01.10.2015

CreateThread (Win32) и AfxBeginThread (MFC) са два начина да го направите.

Така или иначе вашият подпис MyMethod ще трябва да се промени малко.

Редактиране: както е отбелязано в коментарите и от други респонденти, CreateThread може да е лошо.

_beginthread и _beginthreadex са функциите на C библиотеката по време на изпълнение и според документи са еквивалентни на System::Threading::Thread: :Старт

person crashmstr    schedule 27.02.2009
comment
IIRC, CreateThread API вероятно е лоша идея, той не инициализира CRT. - person Daniel Earwicker; 27.02.2009
comment
Ако се свържете динамично с CRT, CreateThread ще накара новата нишка да инициализира CRT в съобщението за прикачване на нишка. - person Michael; 28.02.2009

Помислете за използване на пула от нишки на Win32, вместо да създавате нови нишки за работни елементи. Разпръскването на нови нишки е разточително - всяка нишка получава 1 MB запазено адресно пространство за своя стек по подразбиране, изпълнява кода за стартиране на системната нишка, кара известията да се доставят до почти всяка DLL във вашия процес и създава друг обект на ядрото. Пуловете от теми ви позволяват да използвате повторно нишки за фонови задачи бързо и ефективно и ще нарастват или намаляват в зависимост от това колко задачи изпращате. Като цяло, помислете за завъртане на специални нишки за безкрайни фонови задачи и използвайте пула от нишки за всичко останало.

Преди Vista можете да използвате QueueUserWorkItem. Във Vista, новите API за набор от нишки са по-надеждни и предлагат няколко по-разширени опции. Всеки от тях ще накара вашия фонов код да започне да се изпълнява в някоя нишка от пул от нишки.

// Vista
VOID CALLBACK MyWorkerFunction(PTP_CALLBACK_INSTANCE instance, PVOID context);

// Returns true on success.
TrySubmitThreadpoolCallback(MyWorkerFunction, context, NULL);

// Pre-Vista
DWORD WINAPI MyWorkerFunction(PVOID context);

// Returns true on success
QueueUserWorkItem(MyWorkerFunction, context, WT_EXECUTEDEFAULT);
person Michael    schedule 27.02.2009

Простата нишка в C++ е противоречие!

Разгледайте нишките за усилване за най-близкото нещо до прост подход, наличен днес.

За минимален отговор (който всъщност няма да ви предостави всички неща, от които се нуждаете за синхронизация, но отговаря буквално на въпроса ви) вижте:

http://msdn.microsoft.com/en-us/library/kdzttdcb(VS.80).aspx

Също така static означава нещо различно в C++.

person Daniel Earwicker    schedule 27.02.2009

Това безопасно ли е:

unsigned __stdcall myThread(void *ArgList) {
//Do stuff here
}

_beginthread(myThread, 0, &data);

Трябва ли да направя нещо, за да освободя паметта (като CloseHandle) след това повикване?

person Jon Tackabury    schedule 27.02.2009
comment
Не - компилаторът ще се погрижи за това вместо вас. - person Christopher Dolan; 27.02.2009
comment
Как бих използвал CloseHandle, ако просто създам тази нишка и тя излезе извън обхвата? Просто искам да го започна и да го забравя. - person Jon Tackabury; 27.02.2009
comment
В документите се казва, че _endthread се извиква автоматично, ако функцията на нишката излезе - person crashmstr; 28.02.2009

Друга алтернатива е pthreads - те работят както на windows, така и на linux!

person sean riley    schedule 16.03.2009

CreateThread (Win32) и AfxBeginThread (MFC) са два начина да го направите.

Все пак внимавайте да използвате _beginthread, ако трябва да използвате библиотеката за изпълнение на C (CRT).

person rotoglup    schedule 27.02.2009
comment
beginthreadex е много по-безопасен, тъй като гарантира, че бърза излизаща нишка няма да връща манипулатор на друга нишка, вижте забележките на msdn.microsoft.com/en-us/library/kdzttdcb(vs.71).aspx - person newgre; 27.02.2009

Само за win32 и без допълнителни библиотеки можете да използвате функцията CreateThread http://msdn.microsoft.com/en-us/library/ms682453(VS.85).aspx

person Mykola Golubyev    schedule 27.02.2009

Ако наистина не искате да използвате библиотеки на трети страни (бих препоръчал boost::thread, както е обяснено в другите отговори), трябва да използвате Win32API:

static void MyMethod(int data)
{

    int data = 3;

    HANDLE hThread = ::CreateThread(NULL,
        0,
        &RunStuff,
        reinterpret_cast<LPVOID>(data),
        0,
        NULL);


    // you can do whatever you want here

    ::WaitForSingleObject(hThread, INFINITE);
    ::CloseHandle(hThread);
}

static DWORD WINAPI RunStuff(LPVOID param)
{  

     int data = reinterpret_cast<int>(param);

     //long running operation here

     return 0;
}
person Edouard A.    schedule 27.02.2009
comment
Това изглежда много близо до това, от което се нуждая, но как да извикам CloseHandle, ако не изчакам нишката да завърши? - person Jon Tackabury; 27.02.2009
comment
Коментарът, който казва, че можете да правите каквото искате тук, е малко подвеждащ, нали? За многонишкова програма, която наистина има шанс да работи, трябва да сте доста внимателни какво правите там. - person Daniel Earwicker; 27.02.2009
comment
Jon T, ако не изчакате завършването на нишката, можете да затворите манипулатора веднага: нямате нужда от него. - person Edouard A.; 02.03.2009

person    schedule
comment
Трябва ли да се тревожа за освобождаване на памет по-късно, ако създам нишка като тази? Има ли изтичане на памет само като направите това? - person Jon Tackabury; 27.02.2009
comment
Това е пример за използване на библиотеката за повишаване, а не директно използване на API на Windows. Вижте stackoverflow.com/questions/596360/good-c- lib-for-threading за малко информация относно boost и евентуално други библиотеки за опростяване на нишките в C++ - person Stephen; 27.02.2009
comment
boost::thread използва много малко ресурси, всички от които се почистват при излизане от приложението. Освен това е много по-лесен за използване от функциите CreateThread() или pthread библиотека. - person greyfade; 27.02.2009
comment
Бих избрал boost, тъй като е междуплатформен. - person Sedat Kapanoglu; 27.02.2009
comment
Мисля, че самият boost::thread също е библиотека само за заглавки. Може обаче да зависи от свързването към система за усилване, но не мога да си спомня директно. - person greyfade; 27.02.2009
comment
това едно от планираните за включване в следващата версия на C++ като част от стандартните библиотеки ли е? ако е така, няма да е външна зависимост за дълго... - person rmeador; 27.02.2009
comment
Това приложение ще работи дълго време и ще генерира много нишки. Ще бъде ли проблем почистването на паметта/ресурса? Boost обработва ли почистване след излизане на нишката или трябва да извикам нещо като CloseHandle за всяка нишка? - person Jon Tackabury; 27.02.2009
comment
Създаването на нишка ще бъде стандартно в C++0x и мисля, че ще изглежда по следния начин. Преминаването с boost сега може да улесни пренасянето към C++0x по-късно, ако желаете. - person Dan Olson; 27.02.2009
comment
Той е манипулатор на събития и получава известия от други системи. Всеки път, когато получи известие, искам да разтоваря тази работа в нишка, така че да не блокира повикващия. - person Jon Tackabury; 27.02.2009
comment
Създаването на нишка е толкова скъпо, колкото и заявката за манипулатор на нишка. Почистването е автоматично при унищожаване на обекта boost::thread. - person greyfade; 28.02.2009
comment
@Jon T - звучи сякаш имате нужда от пул от ограничен брой работни нишки, всички работещи от опашка. Ако стартирате неограничен брой нишки въз основа на краткотрайно търсене, програмата ви ще работи зле при голямо натоварване поради прекомерно превключване на контекста. - person Daniel Earwicker; 02.03.2009
comment
би било хубаво, ако добавите необходимата усилваща заглавка към отговора си - person User; 12.07.2012