ตัวอย่างง่ายๆของการเธรดใน C ++


391

ใครบางคนสามารถโพสต์ตัวอย่างง่ายๆของการเริ่มต้นสองหัวข้อ (Object Oriented) ใน C ++

ฉันกำลังมองหาวัตถุเธรด C ++ ที่แท้จริงที่ฉันสามารถขยายวิธีการรันบน (หรือสิ่งที่คล้ายกัน) ซึ่งตรงข้ามกับการเรียกไลบรารีเธรด C-style

ฉันออกจากคำขอเฉพาะระบบปฏิบัติการใด ๆ ด้วยความหวังว่าใครก็ตามที่ตอบกลับจะตอบกลับด้วยห้องสมุดข้ามแพลตฟอร์มที่จะใช้ ตอนนี้ฉันกำลังอธิบายอย่างชัดเจน


คำตอบ:


561

สร้างฟังก์ชั่นที่คุณต้องการให้เธรดดำเนินการเช่น:

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

ข้อมูลเพิ่มเติมเกี่ยวกับมาตรฐาน :: ด้ายที่นี่

  • เมื่อวันที่ GCC -std=c++0x -pthreadรวบรวมกับ
  • สิ่งนี้น่าจะใช้ได้กับระบบปฏิบัติการใด ๆ ที่คอมไพเลอร์ของคุณรองรับคุณสมบัตินี้ (C ++ 11)

4
@ Preza8 VS10 ไม่รองรับคุณสมบัติมากมายใน C ++ 11 เธรดการสนับสนุน VS12 และ VS13
jodag

3
ในความคิดเห็น "รุ่น GCC ด้านล่าง 4.7 ให้ใช้-std=c++0x(แทน-std=c++0x)" ฉันเชื่อว่า "c ++ 0x" ตัวที่สองควรเป็น "c ++ 11" แทน แต่นั่นเป็นไปไม่ได้ที่จะเปลี่ยนเพราะการแก้ไขมีขนาดเล็กเกินไป
zentrunix

1
@MasterMastic มันแตกต่างกันอย่างไรถ้าแทน std :: thread t1 (task1, "Hello") เราใช้ std :: thread t1 (& task1, "Hello") ผ่านการอ้างอิงของฟังก์ชัน? เราควรจะประกาศฟังก์ชั่นเป็นตัวชี้ในกรณีนี้หรือไม่?
user2452253

3
@ user2452253 หากคุณผ่าน "task1" คุณจะส่งตัวชี้ไปยังฟังก์ชันที่คุณต้องการใช้งาน (ซึ่งถือว่าดี) ถ้าคุณผ่าน "& task1" คุณผ่านตัวชี้ไปยังตัวชี้ของฟังก์ชันและผลลัพธ์ที่ได้อาจจะไม่สวยและไม่ได้กำหนดมาก (มันจะเป็นเช่นพยายามที่จะทำงานตามคำสั่งสุ่มหนึ่งหลังจากที่อื่น. ด้วยความน่าจะเป็นที่เหมาะสมที่คุณอาจจะเพียงแค่เปิดประตูสู่นรก) คุณเพียงแค่ต้องการส่งผ่านตัวชี้ฟังก์ชั่น (ตัวชี้ที่มีค่าของที่อยู่ที่ฟังก์ชันเริ่มต้น) หากยังไม่ชัดเจนก็แจ้งให้เราทราบและขอให้โชคดี!
MasterMastic

2
@currguy ดีวิธีที่ถูกต้องคือส่งตัวชี้ของฟังก์ชันที่คุณต้องการเรียกใช้ task1ในกรณีนี้ฟังก์ชั่น task1ดังนั้นตัวชี้ฟังก์ชันยังแสดงโดย แต่ขอขอบคุณสำหรับการแจ้งเรื่องนี้ขึ้นมาเพราะผมเชื่อว่าผมผิดและก็เทียบเท่ากับ&task1ดังนั้นมันไม่สำคัญซึ่งรูปแบบที่คุณเลือกที่จะเขียน (ถ้าเป็นเช่นนั้นผมคิดว่าตัวชี้ที่ตัวชี้ของฟังก์ชั่น&&task1- ว่าจะไม่ดี ) คำถามที่เกี่ยวข้อง
MasterMastic

80

ในทางเทคนิคแล้ววัตถุใด ๆ นั้นจะถูกสร้างขึ้นในไลบรารีเธรดสไตล์ C เนื่องจาก C ++ เพิ่งระบุstd::threadโมเดลสต็อคใน c ++ 0x ซึ่งเพิ่งถูกตอกย้ำและยังไม่ถูกนำมาใช้ ปัญหานั้นค่อนข้างเป็นระบบโดยทางเทคนิคแล้วโมเดลหน่วยความจำ c ++ ที่มีอยู่นั้นไม่เข้มงวดพอที่จะอนุญาตให้มีความหมายที่กำหนดไว้อย่างดีสำหรับกรณี 'เกิดขึ้นก่อนหน้า' ทั้งหมด Hans Boehm เขียนบทความในหัวข้อย้อนหลังและมีส่วนช่วยในการตอกย้ำมาตรฐาน c ++ 0x ในหัวข้อ

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

ที่กล่าวว่ามีหลายไลบรารี C ++ เธรดข้ามแพลตฟอร์มที่ทำงานได้ดีในทางปฏิบัติ หน่วยการสร้างเธรด Intel ประกอบด้วยออบเจ็กต์ tbb :: thread ที่ใกล้เคียงกับมาตรฐาน c ++ 0x อย่างใกล้ชิดและ Boost มีบูสเตอร์เพิ่ม :: เธรดที่ทำเช่นเดียวกัน

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

8
Boost thread is great - ปัญหาเดียวของฉันคือคุณไม่สามารถ (เมื่อฉันใช้ครั้งล่าสุด) เข้าถึงการจัดการเธรดพื้นฐานดั้งเดิมเนื่องจากเป็นสมาชิกคลาสส่วนตัว! มีตันของสิ่งต่าง ๆ ใน win32 ที่คุณต้องการใช้เธรดสำหรับดังนั้นเราจึง tweaked เพื่อให้การจัดการสาธารณะ
Orion Edwards

4
ปัญหาอีกประการหนึ่งของ boost :: thread คือว่าฉันจำได้ว่าคุณไม่มีอิสระในการตั้งขนาดสแต็คของเธรดใหม่ - คุณลักษณะที่น่าเศร้าที่ไม่รวมอยู่ในมาตรฐาน c ++ 0x
Edward KMETT

รหัสนั้นให้ฉันเพิ่ม :: ข้อยกเว้นคัดลอกไม่ได้สำหรับบรรทัดนี้: thread thread_1 = thread (task1); อาจเป็นเพราะฉันใช้เวอร์ชั่นที่เก่ากว่า (1.32)
Frank

task1 ไม่ถูกประกาศในขอบเขตนี้?
Jake Gaston

20

นอกจากนี้ยังมีไลบรารี 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


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

13

เมื่อค้นหาตัวอย่างของคลาส 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
    }
}

โปรดทราบว่าตัวอย่างนี้ไม่เข้าสู่ mutex หรือล็อค


2
ขอขอบคุณที่โพสต์สิ่งนี้ โปรดทราบว่ารูปแบบทั่วไปของการเรียกใช้เธรดในวิธีการอินสแตนซ์จะเป็นดังนี้: Foo f; std :: thread t (& Foo :: Run, & f, args ... ); (โดยที่ Foo เป็นคลาสที่มี 'Run ()' เป็นฟังก์ชันสมาชิก)
Jay Elston

ขอขอบคุณที่โพสต์สิ่งนี้ ฉันไม่เข้าใจอย่างจริงจังว่าทำไมเกลียวตัวอย่างทั้งหมดจึงใช้ฟังก์ชันระดับโลก
hkBattousai

9

ถ้าไม่มีใครต้องการฟังก์ชั่นที่แยกจากกันใน namespac ทั่วโลกเราสามารถใช้ฟังก์ชั่นแลมบ์ดาสำหรับการสร้างกระทู้

หนึ่งในข้อได้เปรียบที่สำคัญของการสร้างเธรดโดยใช้แลมบ์ดาคือเราไม่จำเป็นต้องผ่านพารามิเตอร์ท้องถิ่นเป็นรายการอาร์กิวเมนต์ เราสามารถใช้รายการจับภาพสำหรับสิ่งเดียวกันและคุณสมบัติการปิดของแลมบ์ดาจะดูแลวงจรชีวิต

นี่คือตัวอย่างรหัส

int main() {
    int localVariable = 100;

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

    th.join();

    return 0;
}

ถึงตอนนี้ฉันได้พบว่า C ++ lambdas นั้นเป็นวิธีที่ดีที่สุดในการสร้างเธรดโดยเฉพาะสำหรับฟังก์ชั่นเธรดที่ง่ายขึ้น


8

ส่วนใหญ่ขึ้นอยู่กับห้องสมุดที่คุณตัดสินใจใช้ ตัวอย่างเช่นถ้าคุณใช้ไลบรารี 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 คุณจะต้องสร้างเธรดใหม่ที่จะเริ่มประมวลผลรหัสในวิธีการ "เข้าร่วม" ของคุณ คุณจะต้องอ้างอิงถึงหัวข้อในกรณีส่วนใหญ่เพื่อเข้าร่วมหรือหยุดมัน ข้อมูลเพิ่มเติมที่นี่: เอกสาร wxThread


1
คุณลืมที่จะลบกระทู้ บางทีคุณอาจต้องการสร้างโฆษณาเดี่ยวเธรด
aib

1
ใช่แล้วฉันดึงรหัสออกมาจากรหัสบางรหัสที่ฉันกำลังทำงานอยู่และแน่นอนว่าการอ้างอิงไปยังเธรดถูกเก็บไว้ที่ใดที่หนึ่งเพื่อเข้าร่วมหยุดและลบในภายหลัง ขอบคุณ :)
LorenzCK
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.