C ++ 'ta iş parçacığına basit örnek


391

Birisi C ++ 'da iki (Nesne Tabanlı) iş parçacığı başlatmak için basit bir örnek gönderebilir.

Bir C-tarzı iş parçacığı kütüphanesi çağırmak yerine çalışma yöntemleri (veya benzer bir şey) genişletebilirsiniz gerçek C ++ iş parçacığı nesneleri arıyorum.

Yanıtlayanların kullanmak için çapraz platform kitaplıklarıyla yanıt vermesini umarak işletim sistemine özgü tüm istekleri hariç tuttum. Bunu şimdi açık bir şekilde ifade ediyorum.


Yanıtlar:


561

İş parçacığının yürütmesini istediğiniz bir işlev oluşturun, örneğin:

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

Şimdi threadsonuçta yukarıdaki işlevi aşağıdaki gibi çağıracak nesneyi oluşturun :

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

( Sınıfa #include <thread>erişmeniz gerekiyor std::thread)

Yapıcı argümanları, iş parçacığının yürüteceği işlevdir, bunu işlevin parametreleri izler. İplik, yapım sonrasında otomatik olarak başlatılır.

Daha sonra işlevi yürütmek için iş parçacığının yapılmasını beklemek istiyorsanız, arayın:

t1.join(); 

(Birleştirme, yeni iş parçacığını çağıran iş parçacığının, kendi işleyişine devam etmeden önce yeni iş parçacığının yürütmeyi bitirmesini bekleyeceği anlamına gelir).


Kod

#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 here hakkında daha fazla bilgi

  • GCC'de ile derleyin -std=c++0x -pthread.
  • Bu, herhangi bir işletim sistemi için çalışmalıdır, derleyicinizin bu (C ++ 11) özelliğini desteklemesi gerekir.

4
@ Preza8 VS10, C ++ 11'deki pek çok özelliği desteklemez. VS12 ve VS13 destek dişleri.
jodag

3
"4.7 altındaki GCC sürümleri, kullanın -std=c++0x(yerine -std=c++0x)" İkinci "c ++ 0x" yerine "c ++ 11" olması gerektiğine inanıyorum, ancak düzenleme çok küçük olduğu için değiştirmek mümkün değildir.
zentrunix

1
@MasterMastic Fonksiyonun referansını geçerek std :: thread t1 (görev1, "Hello") yerine std :: thread t1 (& task1, "Hello") kullanırsak ne fark eder? Bu durumda işlevi bir işaretçi olarak ilan etmeli miyiz?
user2452253

3
@ user2452253 "Görev1" iletirseniz, yürütmek istediğiniz işleve bir işaretçiyi iletirsiniz (ki bu iyidir). Eğer "& Görev1" geçerseniz size bir fonksiyonun bir işaretçi bir işaretçi geçmek ve sonuç muhtemelen çok güzel ve çok tanımsız olmaz (rastgele talimatları birbiri ardına yürütmeye çalışırken gibi olurdu. Doğru olasılık sen ile sadece kudreti cehenneme açılan kapıyı aç). Gerçekten işlev işaretçisini (işlevin başladığı adresin değerine sahip bir işaretçi) geçmek istiyorsunuz. Eğer bu bir şeyleri netleştirmezse, bana bildirin ve iyi şanslar!
MasterMastic

2
@curiousguy Bunu yapmanın doğru yolu, çalıştırmak istediğiniz işlevin bir işaretçisini iletmektir. Bu durumda işlev task1. Böylece işlev işaretçisi de ile gösterilir task1. Ama bunu getirdiğiniz için teşekkür ederim çünkü yanlış olduğuma ve eşdeğer olduğuna inanıyorum &task1, bu yüzden hangi formu yazmayı seçtiğiniz önemli değil (eğer öyleyse, işlevin işaretçisine işaretçi olduğunu düşünüyorum &&task1- bu kötü olurdu) ). İlgili Soru .
MasterMastic

80

Teknik olarak, bu tür herhangi bir nesne bir C-tarzı iş parçacığı kütüphanesi üzerine inşa edilecek, çünkü C ++ sadece std::threadc ++ 0x'de sadece çivilenmiş ve henüz uygulanmamış bir stok modeli belirledi . Sorun biraz sistemiktir, teknik olarak mevcut c ++ bellek modeli, 'daha önce olur' vakalarının tümü için iyi tanımlanmış anlambilime izin verecek kadar katı değildir. Hans Boehm bir süre önce konuyla ilgili bir makale yazdı ve konuyla ilgili c ++ 0x standardını ortaya çıkarmada etkili oldu.

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

Bu pratikte iyi çalışan birkaç platformlar arası iş parçacığı C ++ kütüphaneleri olduğunu söyledi. Intel iş parçacığı yapı taşları, c ++ 0x standardına yakın bir şekilde yaklaşan bir tbb :: iş parçacığı nesnesi içerir ve Boost, bunu yapan bir boost :: iş parçacığı kitaplığına sahiptir.

http://www.threadingbuildingblocks.org/

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

Boost :: thread kullanarak şöyle bir şey elde edersiniz:

#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 iş parçacığı harika - benim tek sorunum (en son kullandığımda) aslında özel bir sınıf üyesi olduğu gibi altta yatan iplik tanıtıcı erişemedi oldu! Win32'de iş parçacığına ihtiyacınız olan bir ton TON var, bu yüzden kolu genelleştirmek için ayarladık.
Orion Edwards

4
Boost :: thread ile başka bir sorun, hatırladığım gibi yeni iş parçacığının yığın boyutunu ayarlama özgürlüğüne sahip değilsiniz - c ++ 0x standardında da lamentably dahil olmayan bir özellik.
Edward KMETT

Bu kod bana bu satır için bir boost :: kopyalanamayan istisna veriyor: thread thread_1 = thread (task1); Belki de eski bir versiyonunu kullanıyorum (1.32).
Frank

task1 bu kapsamda bildirilmedi mi?
Jake Gaston

20

POSIX işletim sistemleri için bir POSIX kütüphanesi de vardır. Uyumluluğu kontrol edin

#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 ile derle

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

Yeni bir iş parçacığında kendi örnek yöntemlerinden birini çağıran bir C ++ sınıfı örneği ararken, bu soru ortaya çıkıyor, ancak bu cevapların hiçbirini bu şekilde kullanamadık. İşte bunu yapan bir örnek:

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

Bu örneğin muteks veya kilitleme işlemine girmediğini unutmayın.


2
Bunu gönderdiğiniz için teşekkürler. Örnek yöntemde bir iş parçacığını çağırmanın genel biçiminin şöyle bir şey olduğunu unutmayın: Foo f; std :: thread t (& Foo :: Çalıştır, & f, args ...); (burada Foo, üye işlevi olarak 'Run ()' içeren bir sınıftır).
Jay Elston

Bunu gönderdiğiniz için teşekkürler. Tüm iş parçacığı örneklerinin neden küresel işlevleri kullandığını ciddi olarak anlamıyorum.
hkBattousai

9

Global ad boşluklarında ayrı bir işlev istemedikçe, iş parçacığı oluşturmak için lambda işlevlerini kullanabiliriz.

Lambda kullanarak iş parçacığı oluşturmanın en büyük avantajlarından biri, yerel parametreleri bağımsız değişken listesi olarak geçirmemiz gerekmemesidir. Yakalama listesini aynı şekilde kullanabiliriz ve lambda'nın kapanış özelliği yaşam döngüsüyle ilgilenir.

İşte bir örnek kod

int main() {
    int localVariable = 100;

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

    th.join();

    return 0;
}

Şimdiye kadar, C ++ lambdas'ın özellikle daha basit iplik işlevleri için iplik oluşturmanın en iyi yolu olduğunu gördüm


8

Büyük ölçüde kullanmaya karar verdiğiniz kütüphaneye bağlıdır. Örneğin, wxWidgets kitaplığını kullanırsanız, bir iş parçacığının oluşturulması şöyle görünür:

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

Ana iş parçacığınız CreateThread yöntemini çağırırsa, kodu "Giriş" yönteminizde yürütmeye başlayacak yeni bir iş parçacığı oluşturacaksınız. Çoğu durumda, iş parçacığına katılmak veya durdurmak için ileti dizisine bir başvuruda bulunmanız gerekir. Daha fazla bilgi için: wxThread belgeleri


1
İleti dizisini silmeyi unuttunuz. Belki de bağımsız bir iş parçacığı oluşturmak istediniz ?
aib

1
Evet, şu anda üzerinde çalıştığım bazı kodlardan kodu çıkardım ve elbette iş parçacığına referans katılmak, durdurmak ve daha sonra silmek için bir yerde saklanır. Teşekkürler. :)
LorenzCK
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.