C++ 对象和线程

Posted

技术标签:

【中文标题】C++ 对象和线程【英文标题】:C++ objects and threads 【发布时间】:2014-10-30 09:34:52 【问题描述】:

我正在做一个简单的服务器应用程序,你可以有多个连接,每个连接都是一个线程。这是我希望它看起来像的示例(它不起作用),有一个线程集合,其中每个线程实例化一个类连接的对象:

class connection;

class server
std::vector<std::thread> active_connections;
public:
    void listen() active_connections.push_back(std::thread(connection));
;

我一直在寻找解决方案,但我能找到的最好的是一些成员函数线程。当我测试它时,解决方案变得非常错误,例如:

class connection;

class server
std::vector<std::thread> active_connections;
public:
    void new_connection()  ... 
    void listen()  
        active_connections.push_back(std::thread(&server::new_connection,this)); 
    ;

消息是:错误:使用已删除的函数‘std::thread::thread(const std::thread&)。 这是否意味着 std::thread 类想要复制服务器类?我不太懂 C++,所以请不要发火,我只是在问。

谢谢!

编辑:

这就是发生这种情况的地方:

void server::do_listen()
   
    int addr_size = sizeof(sockaddr_in);
    sockaddr_in client_sock;
    connection_info cn_info;

    while(true)
    
        int csock;
        if((csock = accept(server_sock, (sockaddr*)&client_sock, (socklen_t*)&addr_size)) != -1)
        
            printf("Incomming connection from %s.\n", inet_ntoa(client_sock.sin_addr));
            memset(&cn_info,0, sizeof(connection_info));
            cn_info.sock_addr = client_sock;
            cn_info.sock = csock;
            std::thread thr(&server::new_connection, *this, cn_info);
            thr.join();
        

    

到目前为止。 server::new_connection() 仍然是空的。

【问题讨论】:

Error C2280: 'std::thread::thread(const std::thread &)' : attempting to reference a deleted function的可能重复 这意味着有东西试图复制一个不可复制的thread 对象。但是您发布的代码并没有这样做,并且一旦我修复了明显的错误就会编译(active_connections 应该是一个变量,而不是一个函数)。请发布一个最小的测试用例来演示您遇到的问题。 【参考方案1】:

问题出在这里:

std::thread thr(&server::new_connection, *this, cn_info);
                                         ^

您正在绑定服务器对象的副本;这是不可复制的,因为它包含(一个容器)不可复制的thread 对象。相反,绑定一个指针:

std::thread thr(&server::new_connection, this, cn_info);

有些人可能会发现 lambda 更具可读性;这会按值捕获this 指针和cn_info

std::thread thr([=]new_connection(cn_info););

正如评论者所说,您可以通过绑定reference wrapper 来混淆解决方案:

std::thread thr(&server::new_connection, std::ref(*this), cn_info);

但我更愿意尽可能消除而不是增加复杂性。

【讨论】:

+1 但我们还要提一下 std::refstd::cref en.cppreference.com/w/cpp/utility/functional/ref @Peter:如果你这么说的话。它只是与这个问题的外围相关,在这里不需要,除非你对不必要的复杂性有一种迷恋,但无论如何我已经提到了。【参考方案2】:

这是否意味着 std::thread 类要复制服务器类?

不,这意味着在某处制作了 std::thread 的副本,但这是禁止的,因为 std::thread 在 NonCopyable 中(请注意构造函数列表中的 thread(const thread&amp;) = delete;)。

您应该删除任何执行thread 副本的代码。您发布的那个不执行此类复制。

在“幕后”制作副本的示例是线程变量向量中的push_back,即:

std::thread myThread;
myVector.push_back(myThread);

在您的代码中:

active_connections.push_back(std::thread(&server::new_connection,this)); 

当你推回一个临时的,它不会被复制而是移动到向量中。

【讨论】:

好吧,这个措辞不正确。我的意思是push_back 操作(push_back(std::thread(&amp;server::new_connection,this));)没有复制。将编辑。【参考方案3】:

向您作者提出更多设计问题:

您为一个连接创建一个线程。如果你有十个连接,你就有十个线程。但是,如果您要处理 10000 个连接,您会怎么做?我不确定你的操作系统是否喜欢这样。

如果您只有 N 个线程来处理您的网络连接,则更好的连接处理方法(仅在我看来)是。如果您的计算机具有例如六个核心,您可以只为网络内容花费一个、两个或更多线程。所以你有一个特定大小的线程池。

前摄器模式对您来说可能很有趣:

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

如果您正在寻找可以处理此类事情的库,您可以查看即将推出的名为 Boost.Asynchronous 的 C++ Boost 库:

https://github.com/henry-ch/asynchronous

这个库仍在开发中,但非常好。我已经使用该库制作了一些简单的客户端/服务器应用程序。

【讨论】:

或者他可以使用现有的boost库asio:boost.org/doc/libs/1_56_0/doc/html/boost_asio.html 当然,Boost.Asio 目前可用。但是 Boost.Asio 有一些讨厌的错误。使用 Boost.Asio 编写一个处理多个连接的服务器并不是一件有趣的事情 ;-)

以上是关于C++ 对象和线程的主要内容,如果未能解决你的问题,请参考以下文章

用于组合容器和自定义逻辑的 C++ 技术/库?

线程安全地构造 C++ 对象(MFC CRecordset)

在 C++ 中使用对象的函数创建线程

c++ 当在一个线程中写入并在第二个线程中读取同一个对象时会发生啥? (它安全吗?)[重复]

如何访问与 C++ 中的线程关联的机器状态?

C++的不同对象在线程中会互相干扰吗?