C++11 多线程 —— 传递参数到线程函数

2. 传递参数到线程函数

  • 怎么传递?)Passing arguments to the callable object or function is fundamentally as simple as passing additional arguments to the std::thread constructor. But it’s important to bear in mind that by default the arguments are copied into internal storage, where they can be accessed by the newly created thread of execution, even if the corresponding parameter in the function is expecting a reference. 例如,

    void f(int i, const std::string &s);
    std::thread t(f, 3, "hello");
    

    Note that even though f takes a std::string as the second parameter, the string literal is passed as a const char* and converted to a std::string only in the context of the new thread.
    传递一个指向临时变量的指针时)This is particularly important when the argument supplied is a pointer to an automatic variable, as follows:

    void f(int i, const std::string &s);
    void oops(int some_param) {
    	char buffer[1024];
    	sprintf(buffer, "%i", some_param);
    	std::thread t(f, 3, buffer);
    	t.detach();
    }
    

    问题)In this case, it’s the pointer to the local variable buffer that’s passed through to the new thread, and there’s a significant chance that the function oops will exit before the buffer has been converted to a std::string on the new thread, thus leading to undefined behavior.
    原因)The problem is that you were relying on the implicit conversion of the pointer to the buffer into the std::string object expected as a function parameter, because the std::thread constructor copies the supplied values as is, without converting to the expected argument type.
    解决办法)The solution is to cast to std::string before passing the buffer to the std::thread constructor:

    void not_oops(int some_param) {
    	char buffer[1024];
    	sprintf(buffer,"%i",some_param);
    	std::thread t(f, 3, std::string(buffer));	// 进行显式类型转换,避免悬挂指针
    	t.detach();
    }
    

  • 传递引用
    • 错误的做法!!

      void update_data_for_widget(widget_id w, widget_data &data);		// 传入一个引用
      void oops_again(widget_id w) {
      	widget_data data;
      	std::thread t(update_data_for_widget, w, data);
      	display_status();
      	t.join();
      	process_widget_data(data);
      }
      

      原因)Although update_data_for_widget expects the second parameter to be passed by reference, the std::thread constructor doesn’t know that; it’s oblivious to the types of the arguments expected by the function and blindly copies the supplied values. When it calls update_data_for_widget, it will end up passing a reference to the internal copy of data and not a reference to data itself.
      Consequently, when the thread finishes, these updates will be discarded as the internal copies of the supplied arguments are destroyed, and process_widget_data will be passed an unchanged data rather than a correctly updated version.

    • 正确的做法
      The solution will be readily apparent: you need to wrap the arguments that really need to be references in std::ref.

      std::thread t(update_data_for_widget, w, std::ref(data));
      

      then update_data_for_widget will be correctly passed a reference to data rather
      than a reference to a copy of data.


  • 传入一个成员函数)You can pass a member function pointer as the function, provided you supply a suitable object pointer as the first argument:

    class X {
    public:
    	void do_lengthy_work();
    };
    
    X my_x;
    std::thread t(&X::do_lengthy_work, &my_x);
    

    This code will invoke my_x.do_lengthy_work() on the new thread, because the address of my_x is supplied as the object pointer.
    传递成员函数的参数)You can also supply arguments to such a member function call: the third argument to the std::thread constructor will be the first argument to the member function and so forth.


  • 移动
    Another interesting scenario for supplying arguments is where the arguments can’t be copied but can only be moved: the data held within one object is transferred over to another, leaving the original object “empty.”
    When the source object is a temporary, the move is automatic, but when the source is a named value, the transfer must be requested directly by invoking std::move(). 例如,std::unique_ptr 就是不可复制,只可移动的:

    void process_big_object(std::unique_ptr<big_object>);
    
    std::unique_ptr<big_object> p(new big_object);
    p->prepare_data(42);
    
    std::thread t(process_big_object, std::move(p));
    

    By specifying std::move( p) in the std::thread constructor, the ownership of the big_object is transferred first into internal storage for the newly created thread and then into process_big_object.
    std::thread 对象可移动,不可复制Instances of std::thread are movable, and aren’t copyable. This ensures that only one object is associated with a particular thread of execution at any one time while allowing programmers the option of transferring that ownership between objects.

猜你喜欢

转载自blog.csdn.net/fcku_88/article/details/88095728