Contoh sederhana dari threading di C ++

391

Dapatkah seseorang memposting contoh sederhana memulai dua utas (Berorientasi Objek) dalam C ++.

Saya mencari objek ulir C ++ aktual yang dapat saya gunakan untuk menjalankan metode (atau yang serupa) sebagai lawan memanggil pustaka thread gaya-C.

Saya mengabaikan permintaan khusus OS dengan harapan bahwa siapa pun yang menjawab akan membalas dengan pustaka lintas platform untuk digunakan. Saya hanya membuat itu eksplisit sekarang.

Zak
sumber

Jawaban:

561

Buat fungsi yang Anda ingin utas dieksekusi, misalnya:

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

Sekarang buat threadobjek yang pada akhirnya akan memanggil fungsi di atas seperti:

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

(Anda perlu #include <thread>mengakses std::threadkelas)

Argumen konstruktor adalah fungsi yang akan dijalankan thread, diikuti oleh parameter fungsi. Utas secara otomatis dimulai setelah konstruksi.

Jika nanti Anda ingin menunggu utas selesai menjalankan fungsi, hubungi:

t1.join(); 

(Bergabung berarti bahwa utas yang memanggil utas baru akan menunggu utas baru untuk menyelesaikan eksekusi, sebelum itu akan melanjutkan eksekusinya sendiri).


Kode

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

Informasi lebih lanjut tentang std :: utas di sini

  • Pada GCC, kompilasi dengan -std=c++0x -pthread.
  • Ini harus bekerja untuk sistem operasi apa pun, mengingat kompiler Anda mendukung fitur (C ++ 11) ini.
MasterMastic
sumber
4
@ Preza8 VS10 tidak mendukung banyak fitur di C ++ 11. VS12 dan VS13 mendukung utas.
jodag
3
Dalam komentar "versi GCC di bawah 4.7, gunakan -std=c++0x(bukan -std=c++0x)" Saya percaya yang kedua "c ++ 0x" sebaliknya harus "c ++ 11", tetapi itu tidak mungkin untuk diubah karena hasil edit terlalu kecil.
zentrunix
1
@MasterMastic Apa bedanya jika alih-alih std :: thread t1 (task1, "Hello") kita menggunakan std :: thread t1 (& task1, "Hello"), meneruskan referensi fungsi? Haruskah kita mendeklarasikan fungsi sebagai pointer dalam kasus ini?
user2452253
3
@ user2452253 Jika Anda melewatkan "task1", ​​Anda meneruskan sebuah pointer ke fungsi yang ingin Anda jalankan (yang bagus). Jika Anda meneruskan "& task1" Anda meneruskan sebuah pointer ke sebuah pointer dari suatu fungsi, dan hasilnya mungkin tidak akan begitu cantik dan sangat tidak terdefinisi (itu akan seperti mencoba untuk mengeksekusi instruksi acak satu demi satu. Dengan probabilitas yang tepat Anda mungkin saja buka pintu gerbang ke neraka). Anda benar-benar hanya ingin melewatkan pointer fungsi (pointer dengan nilai alamat tempat fungsi dimulai). Jika itu tidak beres, beri tahu saya, dan semoga beruntung!
MasterMastic
2
@curiousguy Nah, cara yang tepat untuk melakukannya adalah dengan melewatkan pointer dari fungsi yang ingin Anda jalankan. Dalam hal ini fungsinya adalah task1. Jadi pointer fungsi juga dilambangkan dengan task1. Tetapi terima kasih telah mengemukakan ini karena saya yakin saya salah dan ini setara dengan &task1, jadi tidak masalah bentuk mana yang Anda pilih untuk ditulis (jika demikian, saya rasa pointer ke pointer dari fungsi ini &&task1- itu akan menjadi buruk ). Pertanyaan yang relevan .
MasterMastic
80

Nah, secara teknis setiap objek seperti itu akan dibangun di atas pustaka thread gaya-C karena C ++ baru saja menentukan std::threadmodel stok dalam c ++ 0x, yang baru saja dipakukan dan belum diimplementasikan. Masalahnya agak sistemik, secara teknis model memori c ++ yang ada tidak cukup ketat untuk memungkinkan semantik yang didefinisikan dengan baik untuk semua kasus 'terjadi sebelum'. Hans Boehm menulis makalah tentang topik beberapa waktu lalu dan berperan dalam memalu standar c ++ 0x pada topik tersebut.

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

Yang mengatakan ada beberapa cross-platform thread C ++ perpustakaan yang berfungsi dengan baik dalam praktek. Blok pembangun utas Intel berisi objek tbb :: thread yang mendekati standar c ++ 0x dan Boost memiliki pustaka boost :: thread yang melakukan hal yang sama.

http://www.threadingbuildingblocks.org/

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

Menggunakan boost :: utas Anda akan mendapatkan sesuatu seperti:

#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
sumber
8
Tingkatkan utas hebat - satu-satunya masalah saya adalah Anda tidak bisa (ketika saya terakhir menggunakannya) benar-benar mengakses pegangan ulir asli yang mendasari karena itu adalah anggota kelas pribadi! Ada banyak hal di win32 yang Anda butuhkan untuk menangani thread, jadi kami men-tweak-nya untuk menjadikannya pegangan publik.
Orion Edwards
4
Masalah lain dengan boost :: thread adalah seingat saya Anda tidak memiliki kebebasan untuk mengatur ukuran stack dari thread baru - fitur yang juga tidak dimasukkan dalam standar c ++ 0x.
Edward KMETT
Kode itu memberi saya dorongan :: pengecualian yang tidak dapat disalin untuk baris ini: utas thread_1 = utas (task1); Mungkin itu karena saya menggunakan versi yang lebih lama (1,32).
Frank
task1 tidak dideklarasikan dalam lingkup ini?
Jake Gaston
20

Ada juga perpustakaan POSIX untuk sistem operasi POSIX. Periksa kompatibilitas

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

}

kompilasi dengan -lpthread

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

Hohenheimsenberg
sumber
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
sumber
13

Saat mencari contoh kelas C ++ yang memanggil salah satu metode turunannya sendiri di utas baru, pertanyaan ini muncul, tetapi kami tidak dapat menggunakan jawaban ini dengan cara itu. Berikut ini contoh yang melakukan itu:

Hr

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

Perhatikan bahwa contoh ini tidak masuk ke mutex atau mengunci.

livingtech
sumber
2
Terima kasih telah memposting ini. Perhatikan bahwa bentuk umum dari pemanggilan utas metode instan berjalan seperti ini: Foo f; std :: utas t (& Foo :: Jalankan, & f, args ...); (di mana Foo adalah kelas yang memiliki 'Jalankan ()' sebagai fungsi anggota).
Jay Elston
Terima kasih telah memposting ini. Saya benar-benar tidak mengerti mengapa semua contoh threading menggunakan fungsi global.
hkBattousai
9

Kecuali ada yang menginginkan fungsi terpisah dalam namespac global, kita bisa menggunakan fungsi lambda untuk membuat utas.

Salah satu keuntungan utama membuat utas menggunakan lambda adalah kita tidak perlu memberikan parameter lokal sebagai daftar argumen. Kita dapat menggunakan daftar tangkap untuk hal yang sama dan properti penutupan lambda akan menjaga siklus hidup.

Berikut ini contoh kode

int main() {
    int localVariable = 100;

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

    th.join();

    return 0;
}

Sejauh ini, saya telah menemukan C ++ lambdas menjadi cara terbaik untuk membuat utas terutama untuk fungsi utas yang lebih sederhana

Daksh
sumber
8

Ini sangat tergantung pada perpustakaan yang Anda putuskan untuk digunakan. Misalnya, jika Anda menggunakan pustaka wxWidgets, pembuatan utas akan terlihat seperti ini:

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

Jika utas utama Anda memanggil metode CreateThread, Anda akan membuat utas baru yang akan mulai mengeksekusi kode dalam metode "Entri" Anda. Anda harus menyimpan referensi ke utas dalam banyak kasus untuk bergabung atau menghentikannya. Info lebih lanjut di sini: dokumentasi wxThread

LorenzCK
sumber
1
Anda lupa menghapus utas. Mungkin Anda bermaksud membuat utas terpisah ?
aib
1
Yup benar, saya mengekstrak kode dari beberapa kode yang sedang saya kerjakan dan tentu saja referensi ke utas disimpan di suatu tempat untuk bergabung, berhenti dan hapus nanti. Terima kasih. :)
LorenzCK