İş parçacığı işleviyle başlat


294

Ben std::threadbir argüman ve döndüren bir üye işlevi ile inşa etmeye çalışıyorum void. Çalışan herhangi bir sözdizimini bulamıyorum - derleyici ne olursa olsun şikayet ediyor. Çalıştırmak spawn()için bir döndürmek böylece uygulamak için doğru yolu nedirstd::threadtest() ?

#include <thread>
class blub {
  void test() {
  }
public:
  std::thread spawn() {
    return { test };
  }
};

1
U fonksiyon void olarak adlandırılan void döndürür mü yoksa sadece herhangi bir parametresi yok mu demektir? Yapmaya çalıştığınız şeyin kodunu ekleyebilir misiniz?
Zaid Amir

Test yaptın mı? (Henüz yapmadım.) Kodunuz RVO'ya (dönüş-değer-optimizasyon) güveniyor gibi görünüyor, ancak bunu yapmanız gerektiğini düşünmüyorum. Bence kullanmak std::move( std::thread(func) );daha iyi, çünkü std::threadbir kopya oluşturucuya sahip değil.
RnMss

4
@RnMss: RVO'ya güvenebilirsiniz,std::move bu durumda kullanmak gereksizdir - bu doğru değildi ve kopya oluşturucu yoktu, derleyici yine de bir hata verecektir.
Qualia

Yanıtlar:


367
#include <thread>
#include <iostream>

class bar {
public:
  void foo() {
    std::cout << "hello from member function" << std::endl;
  }
};

int main()
{
  std::thread t(&bar::foo, bar());
  t.join();
}

EDIT: Düzenleme muhasebe, böyle yapmak zorunda:

  std::thread spawn() {
    return std::thread(&blub::test, this);
  }

GÜNCELLEME: Bazı noktaları daha fazla açıklamak istiyorum, bazıları da yorumlarda tartışıldı.

Yukarıda açıklanan sözdizimi INVOKE tanımı (§20.8.2.1) açısından tanımlanmıştır:

INVOKE'u (f, t1, t2, ..., tN) aşağıdaki gibi tanımlayın:

  • (t1. * f) (t2, ..., tN) f, T sınıfının üye işlevinin bir göstergesidir ve t1, T türündeki bir nesne veya T türündeki bir nesneye yapılan başvuru veya T'den türetilmiş tipte bir nesne;
  • ((* t1). * f) (t2, ..., tN) f, T sınıfının üye işlevinin bir göstergesiyse ve t1, önceki öğede açıklanan türlerden biri değilse;
  • t1. * f, N == 1 ve f, T sınıfının üye verilerine işaretçi olduğunda ve t 1, T türündeki
    bir nesne veya T türündeki bir nesneye yapılan başvuru veya
    türetilmiş türden bir nesneye yapılan başvurudır T;
  • (* t1). * f, N == 1 ve f, T sınıfının üye verilerine işaretçi olduğunda ve t 1, önceki öğede açıklanan tiplerden biri değildir;
  • f (t1, t2, ..., tN) diğer tüm durumlarda.

Ben işaret etmek istiyorum başka bir genel gerçeği varsayılan olarak iş parçacığı yapıcı kendisine geçirilen tüm bağımsız değişkenleri kopyalayacak olmasıdır. Bunun nedeni, argümanların çağıran evreyi geçmesi gerekebileceğidir, argümanların kopyalanması bunu garanti eder. Bunun yerine, gerçekten bir referans iletmek istiyorsanız, std::reference_wrappertarafından oluşturulmuş bir kullanabilirsiniz std::ref.

std::thread (foo, std::ref(arg1));

Bunu yaparak, iş parçacığı üzerinde çalıştığında argümanların hala var olacağını garanti altına alacağınıza söz veriyorsunuz.


Yukarıda belirtilen her şeyin de tatbik edilebileceği Not std::asyncve std::bind.


1
En azından bu şekilde derleniyor. Yine de örneği ikinci argüman olarak geçtiğiniz hakkında hiçbir fikrim yok.
abergmeier

15
@LCID: Kurucunun çoklu bağımsız değişken sürümü std::thread, bağımsız değişkenler geçirilmiş gibi çalışır std::bind. Bir üye işlevini çağırmak için, ilk bağımsız değişkenin std::binduygun türde bir nesneye bir işaretçi, başvuru veya paylaşılan işaretçi olması gerekir.
Dave S

Kurucunun örtük gibi davrandığından nereden alıyorsunuz bind? Bunu hiçbir yerde bulamıyorum.
Kerrek SB

3
@KerrekSB, [thread.thread.constr] p4'ü [func.bind.bind] p3 ile karşılaştırın, anlambilimler oldukça benzerdir, üye işlevlerinin nasıl adlandırıldığını tanımlayan INVOKE sözde kodu açısından tanımlanır
Jonathan Wakely

4
ilk parametre olarak statik üye işlevlerinin sınıfın örneğini almadığını unutmayın (programcı için görünmez), bu nedenle bu yöntemi ham işlev olarak geçirirken derleme ve bildirim uyuşmazlığı sırasında her zaman bir sorunla karşılaşırsınız.
zoska

100

C ++ 11 kullandığınız için lambda-ifade güzel ve temiz bir çözümdür.

class blub {
    void test() {}
  public:
    std::thread spawn() {
      return std::thread( [this] { this->test(); } );
    }
};

çünkü this->atlanabilir, bu kadar kısaltın olabilir:

std::thread( [this] { test(); } )

ya da sadece

std::thread( [=] { test(); } )

8
Genel olarak, std::moveyerel bir değişkeni değere göre döndürürken kullanmamalısınız . Bu aslında RVO'yu engeller. Sadece değere göre döndürürseniz (hamlesiz) derleyici RVO kullanabilir ve eğer standart yoksa, hareket semantiğini çağırması gerektiğini söyler.
zmb

@zmb, kodun VC10'da derlenmesini istemek dışında, dönüş türü CopyConstructable değilse taşımalısınız.
abergmeier

6
RVO hala hareket semantiğinden daha iyi kod üretir ve gitmez.
Jonathan Wakely

2
Dikkatli ol [=]. Bununla yanlışlıkla büyük bir nesneyi kopyalayabilirsiniz. Genel olarak, veya kullanmak için bir kod kokusu . [&][=]
rustyx

3
@ Herkes Herkes burada bir iplik olduğunu unutma. Bu, lambda işlevinin bağlam kapsamını aşabileceği anlamına gelir. Bu nedenle, yakalama yoluyla referans ( [&]) kullanarak , bazı sarkan başvurular gibi hatalar ekleyebilirsiniz. (Örneğin, std::thread spawn() { int i = 10; return std::thread( [&] { std::cout<<i<<"\n"; } ); })
RnMss

29

İşte tam bir örnek

#include <thread>
#include <iostream>

class Wrapper {
   public:
      void member1() {
          std::cout << "i am member1" << std::endl;
      }
      void member2(const char *arg1, unsigned arg2) {
          std::cout << "i am member2 and my first arg is (" << arg1 << ") and second arg is (" << arg2 << ")" << std::endl;
      }
      std::thread member1Thread() {
          return std::thread([=] { member1(); });
      }
      std::thread member2Thread(const char *arg1, unsigned arg2) {
          return std::thread([=] { member2(arg1, arg2); });
      }
};
int main(int argc, char **argv) {
   Wrapper *w = new Wrapper();
   std::thread tw1 = w->member1Thread();
   std::thread tw2 = w->member2Thread("hello", 100);
   tw1.join();
   tw2.join();
   return 0;
}

G ++ ile derleme aşağıdaki sonucu verir

g++ -Wall -std=c++11 hello.cc -o hello -pthread

i am member1
i am member2 and my first arg is (hello) and second arg is (100)

10
OP sorusuyla gerçekten ilgili değil, ama neden Wrapper'ı öbek üzerinde tahsis ediyorsunuz (ve onu yeniden dağıtmıyorsunuz)? java / c # arka planınız var mı?
Alessandro Teruzzi

deleteYığın hafızaya unutmayın :)
Slack Bot

19

@ hop5 ve @RnMss, C ++ 11 lambdas kullanmayı önerdi, ancak işaretçilerle uğraşırsanız, bunları doğrudan kullanabilirsiniz:

#include <thread>
#include <iostream>

class CFoo {
  public:
    int m_i = 0;
    void bar() {
      ++m_i;
    }
};

int main() {
  CFoo foo;
  std::thread t1(&CFoo::bar, &foo);
  t1.join();
  std::thread t2(&CFoo::bar, &foo);
  t2.join();
  std::cout << foo.m_i << std::endl;
  return 0;
}

çıktılar

2

Bu yanıttan yeniden yazılmış örnek şu şekilde olacaktır:

#include <thread>
#include <iostream>

class Wrapper {
  public:
      void member1() {
          std::cout << "i am member1" << std::endl;
      }
      void member2(const char *arg1, unsigned arg2) {
          std::cout << "i am member2 and my first arg is (" << arg1 << ") and second arg is (" << arg2 << ")" << std::endl;
      }
      std::thread member1Thread() {
          return std::thread(&Wrapper::member1, this);
      }
      std::thread member2Thread(const char *arg1, unsigned arg2) {
          return std::thread(&Wrapper::member2, this, arg1, arg2);
      }
};

int main() {
  Wrapper *w = new Wrapper();
  std::thread tw1 = w->member1Thread();
  tw1.join();
  std::thread tw2 = w->member2Thread("hello", 100);
  tw2.join();
  return 0;
}

0

Bazı kullanıcılar zaten cevaplarını verdiler ve çok iyi açıkladılar.

Konu ile ilgili birkaç şey daha eklemek istiyorum.

  1. Functor ve iplik ile çalışma. Lütfen aşağıdaki örneğe bakın.

  2. İş parçacığı, nesneyi geçerken nesnenin kendi kopyasını oluşturur.

    #include<thread>
    #include<Windows.h>
    #include<iostream>
    
    using namespace std;
    
    class CB
    {
    
    public:
        CB()
        {
            cout << "this=" << this << endl;
        }
        void operator()();
    };
    
    void CB::operator()()
    {
        cout << "this=" << this << endl;
        for (int i = 0; i < 5; i++)
        {
            cout << "CB()=" << i << endl;
            Sleep(1000);
        }
    }
    
    void main()
    {
        CB obj;     // please note the address of obj.
    
        thread t(obj); // here obj will be passed by value 
                       //i.e. thread will make it own local copy of it.
                        // we can confirm it by matching the address of
                        //object printed in the constructor
                        // and address of the obj printed in the function
    
        t.join();
    }

Aynı şeyi başarmanın başka bir yolu şöyledir:

void main()
{
    thread t((CB()));

    t.join();
}

Ancak nesneyi başvuru ile iletmek istiyorsanız, aşağıdaki sözdizimini kullanın:

void main()
{
    CB obj;
    //thread t(obj);
    thread t(std::ref(obj));
    t.join();
}
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.