Helpex - Trao đổi & giúp đỡ Đăng nhập
19

Các chương trình của tôi trông giống như bên dưới

#include <iostream>
#include <thread>

class A {
public:
    void foo(int n ) { std::cout << n << std::endl; }
};

int main()
{
    A a;

    std::thread t1(&A::foo, std::ref(a), 100);

    t1.join();
    return 0;
}

Khi tôi biên dịch nó bằng lệnh sau, tôi gặp lỗi

g++ -o main main.cc -lpthread -std=c++11

Lỗi:

In file included from /usr/local/include/c++/4.8.2/thread:39:0,
                  from check.cc:2:
/usr/local/include/c++/4.8.2/functional: In instantiation of ‘struct std::_Bind_simple<std::_Mem_fn<void (A::*)(int)>(std::reference_wrapper<A>, int)>’:
/usr/local/include/c++/4.8.2/thread:137:47:   required from ‘std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (A::*)(int); _Args = {std::reference_wrapper<A>, int}]’
check.cc:13:42:   required from here
/usr/local/include/c++/4.8.2/functional:1697:61: error:no type named ‘type’ in ‘class std::result_of<std::_Mem_fn<void (A::*)(int)>(std::reference_wrapper<A>, int)>’
        typedef typename result_of<_Callable(_Args...)>::type result_type;
                                                              ^
/usr/local/include/c++/4.8.2/functional:1727:9: error:no type named ‘type’ in ‘class std::result_of<std::_Mem_fn<void (A::*)(int)>(std::reference_wrapper<A>, int)>’
          _M_invoke(_Index_tuple<_Indices...>)
          ^
19 hữu ích 0 bình luận 19k xem chia sẻ
24

Đây không phải là nơi thích hợp cho trình bao bọc tham chiếu. Tuy nhiên, một con trỏ đơn giản là đủ và đạt được kết quả mong muốn:

std::thread t1(&A::foo, &a, 100);
24 hữu ích 1 bình luận chia sẻ
11

CHỈNH SỬA: RETRACTION

Kerrek đúng ở đây: Tôi đã nhầm lẫn rằng phương thức std::threadkhởi tạo và std::bindđược thiết kế bởi các giao diện giống hệt nhau. Tuy nhiên, việc chuyển đổi tự động các đối số từ reference_wrapper<A>thành A&chỉ được chỉ định std::bindtrong [func.bind.bind] / 10:

Giá trị của các đối số bị ràng buộc v1, v2, ..., vN và kiểu tương ứng của chúng V1, V2, ..., VNphụ thuộc vào kiểu TiDbắt nguồn từ lệnh gọi đến bindcv -qualifier cv của trình bao bọc cuộc gọi gnhư sau:

  • nếu TiDreference_wrapper<T>, đối số là tid.get()và kiểu của nó ViT&;
  • ...

Vì vậy, việc sử dụng cụ thể reference_wrapper<A>này không được hỗ trợ bởi std::thread, nhưng được hỗ trợ bởi std::bind. Thực tế std::threadhoạt động giống hệt std::bindtrong trường hợp này trong các trình biên dịch khác / cũ hơn là lỗi, không phải hành vi của bản phát hành GCC 4,8 dòng.

Tôi sẽ để lại câu trả lời không chính xác ở đây với lời giải thích này với hy vọng rằng những người khác sẽ không mắc phải sai lầm này trong tương lai.

Câu trả lời ngắn (nhưng KHÔNG ĐÚNG)

Đây rõ ràng là một lỗi trong thư viện tiêu chuẩn đi kèm với GCC 4.8. Mã được biên dịch chính xác bởi:

Câu trả lời dài (và cũng KHÔNG ĐÚNG):

Các tác động của hàm std::threadtạo

template <class F, class ...Args>
explicit thread(F&& f, Args&&... args);

được trình bày chi tiết trong C ++ 11 30.3.1.2 [thread.thread.constr] / 4:

Chuỗi thực thi mới thực thi

INVOKE(DECAY_COPY(std::forward<F>(f)),
       DECAY_COPY(std::forward<Args>(args))...)

với các lời gọi DECAY_COPYđược đánh giá trong luồng xây dựng.

DECAY_COPY được mô tả trong 30.2.6 [thread.decaycopy] / 1:

Ở một số nơi trong Điều khoản này, phép toán DECAY_COPY(x)được sử dụng. Tất cả các cách sử dụng như vậy có nghĩa là gọi hàm decay_copy(x)và sử dụng kết quả, trong đó decay_copyđược định nghĩa như sau:

template <class T> typename decay<T>::type decay_copy(T&& v)
{ return std::forward<T>(v); }

Trong lời gọi trong OP, std::thread t1(&A::foo, std::ref(a), 100);cả ba đối số đều là các giá trị DECAY_COPYsẽ sao chép thành các đối tượng trong môi trường của luồng mới trước lời gọi, có tác dụng được mô tả trong 20.8.2 [func.require] / 1:

Xác định INVOKE(f, t1, t2, ..., tN)như sau:

  • (t1.*f)(t2, ..., tN)khi nào flà một con trỏ đến một hàm thành viên của một lớp Tt1là một đối tượng của kiểu Thoặc một tham chiếu đến một đối tượng của kiểu Thoặc một tham chiếu đến một đối tượng của kiểu dẫn xuất từ T;
  • ((*t1).*f)(t2, ..., tN)khi nào flà một con trỏ đến một hàm thành viên của một lớp Tt1không phải là một trong các kiểu được mô tả trong mục trước;
  • ...

Đối với mã trong OP, flà một con trỏ đến hàm thành viên của lớp Acó giá trị &A::foo, t1là một giá trị reference_wrapper<A>có tham chiếu được lưu trữ tham chiếu đến at2là một intvới giá trị 100. Viên đạn thứ hai của 20.8.2 / 1 được áp dụng. Vì t1là a reference_wrapper, *t1đánh giá tham chiếu được lưu trữ (trên 20.8.3.3/1) và lời gọi trong luồng mới là hiệu quả

(a.*&A::foo)(100);

Vì vậy, có, tiêu chuẩn mô tả hoạt động của OP chính xác như mong đợi.

CHỈNH SỬA: Thật kỳ lạ, GCC 4.8 biên dịch chính xác ví dụ tương tự :

class A {
public:
    void foo(int n) { std::cout << n << std::endl; }
};

int main()
{
    A a;
    auto foo = std::bind(&A::foo, std::ref(a), 100);
    foo();
}
11 hữu ích 3 bình luận chia sẻ
10

Về tiêu đề câu hỏi của bạn, tôi sẽ sử dụng lambda để xây dựng luồng. Có hoặc không có tham chiếu, thông qua việc gọi các hàm thành viên hoặc các tham số ràng buộc.

 std::thread t1([&] { a.foo(100); });
10 hữu ích 1 bình luận chia sẻ
7

GCC 4.8 là đúng std::threadvà các thành phần khác được định nghĩa theo INVOKE không được thực hiện theo std::bind. Chúng không được gọi các biểu thức ràng buộc lồng nhau và phải sử dụng chuyển tiếp hoàn hảo cho các đối số bị ràng buộc (thay vì chuyển tiếp chúng dưới dạng giá trị như std::bindhiện tại) và ngoài ra khi bạn phát hiện ra chúng không mở reference_wrappercác đối tượng. Trong GCC 4.8, tôi đã giới thiệu một chi tiết triển khai nội bộ __bind_simple, để sử dụng bởi std::threadv.v. không có đầy đủ std::bindhành vi.

Mặc dù những khác biệt khác so với std::bindmong muốn, tôi nghĩ rằng hoạt động INVOKE vẫn nên hỗ trợ reference_wrappercác đối tượng, vì vậy tôi đã gửi báo cáo lỗi, xem LWG 2219 .

7 hữu ích 2 bình luận chia sẻ
0

Tôi chỉ muốn nói thêm rằng tôi đã gặp lỗi tương tự chỉ bằng cách đưa ra các đối số không tương thích cho std :: bind / std :: thread. Giống như việc cung cấp một con trỏ cho một lớp cơ sở khi một con trỏ cụ thể hơn nằm trong chữ ký của hàm thực.

0 hữu ích 0 bình luận chia sẻ
0

Ok vấn đề là ref (obj) trả về một tham chiếu (bí danh) cho một đối tượng không phải là một con trỏ (địa chỉ)! để làm việc với các chủ đề, chúng tôi cần con trỏ chứ không phải tham chiếu! Xem Bên dưới một chương trình tiện dụng để sử dụng con trỏ hàm với các luồng:

    #include <iostream>
    #include "vector"
    #include "string"
    #include "thread"
    #include "atomic"
    #include "functional"

    #include "stdlib.h"
    #include "stdio.h"
    #include "string.h"
    #include "assert.h"

    using namespace std;
    //__________________________Global variables_________________________________________________

    atomic<int> var(0);

    //__________________________class____________________________________________________________

    class C
    {
    public:

        C()
        {}

        static void addition (int a, int b)
        {
            for(int i= 0; i< a+b; i++)
                var++;
        }

        void subtraction (int a, int b)
        {
            for(int i= 0; i< a+b; i++)
                var--;
        }
    };

    class D : std::atomic<int>
    {
    public:
        D() : std::atomic<int>(0)
        {}

        void increase_member (int n)
        {
            for (int i=0; i<n; ++i)
                fetch_add(1);
        }

        int get_atomic_val()
        {
            return this->load();
        }
    };

    //________________________________functions________________________________________________

    void non_member_add (int a, int b)
    {
        for(int i= 0; i< a+b; i++)
            var++;
    }

    //__________________________________main____________________________________________________

    int main ()
    {
        int a=1, b=5;

    // (I)...........................................static public member function (with no inheritance).........................................

        void (* add_member_func_ptr)(int,int) = C::addition;            // pointer to a static public member function

        //defining thread pool for ststic public member_add_ptr

        vector<thread> thread_pool;

        for (int i=0; i<5; i++)
        {
            thread_pool.push_back(thread(add_member_func_ptr,a,b));
        }

        for(thread& thr: thread_pool)
            thr.join();

        cout<<"static public member function (with no inheritance)\t"<<var<<endl;

        //defining thread pool for ststic public member function

        var=0;

        thread_pool.clear();

        for (int i=0; i<5; i++)
        {
            thread_pool.push_back(thread(C::addition,a,b));             //void (* add_member_func_ptr)(int,int) is equal to C::addition
        }

        for(thread& thr: thread_pool)
            thr.join();

        cout<<"static public member function (with no inheritance)\t"<<var<<endl;

    // (II)..............................................non-static public member function (with no inheritance)...................................

        C bar;

        void (C::* sub_member_func_ptr)(int,int) = & C::subtraction;            // pointer to a non-static public member function

        var=0;

        //defining thread pool for non-ststic public member function

        thread_pool.clear();

        for (int i=0; i<5; i++)
        {
            thread_pool.push_back(thread(sub_member_func_ptr,bar,a,b));
        }

        for(thread& thr: thread_pool)
            thr.join();

        cout<<"non-static public member function (with no inheritance)\t"<<var<<endl;

        var=0;

        //defining thread pool for non-ststic public member function

        thread_pool.clear();

        for (int i=0; i<5; i++)
        {
            thread_pool.push_back(thread(&C::subtraction,bar,a,b));         //void (C::* sub_member_func_ptr)(int,int) equals & C::subtraction;
        }

        for(thread& thr: thread_pool)
            thr.join();

        cout<<"non-static public member function (with no inheritance)\t"<<var<<endl;


    // (III)................................................non-member function .................................................

        void (* non_member_add_ptr)(int,int) = non_member_add;              //pointer to a non-member function

        var=0;

        //defining thread pool for non_member_add

        thread_pool.clear();

        for (int i=0; i<5; i++)
        {
            thread_pool.push_back(thread(non_member_add,a,b));
        }

        for(thread& thr: thread_pool)
            thr.join();

        cout<<"non-member function\t"<<var<<endl<<endl;

    // (IV)...........................................non-static public member function (with inheritance).........................

        D foo;

        void (D::* member_func_ptr) (int) = & D::increase_member;                  //pointer to a non-static public member function of a derived class

        //defining thread pool for non-ststic public member function of a derived class

        thread_pool.clear();

        for (int i=0; i<5; i++)
        {
            thread_pool.push_back(thread(member_func_ptr,&foo,10));                 //use &foo because this is derived class!
        }

        for(thread& thr: thread_pool)
            thr.join();

        cout<<"non-static public member function (with inheritance)\t"<<foo.get_atomic_val()<<endl;

        //defining thread pool for non-ststic public member function

        D poo;

        thread_pool.clear();

        for (int i=0; i<5; i++)
        {
            reference_wrapper<D> poo_ref= ref(poo);

            D& poo_ref_= poo_ref.get();             //ref(obj) returns a reference (alias) to an object not a pointer(address)!

            D* d_ptr= &poo;                         //to work with thread we need pointers not references!


            thread_pool.push_back(thread(&D::increase_member, d_ptr,10));             //void (D::* member_func_ptr) (int) equals & D::increase_member;
        }

        for(thread& thr: thread_pool)
            thr.join();

        cout<<"non-static public member function (with inheritance)\t"<<poo.get_atomic_val()<<endl<<endl;


        return 0;
    }
0 hữu ích 0 bình luận chia sẻ
loading
Không tìm thấy câu trả lời bạn tìm kiếm? Duyệt qua các câu hỏi được gắn thẻ c++ c++11 , hoặc hỏi câu hỏi của bạn.

Có thể bạn quan tâm

loading