将 RAII 套接字 class 传递给线程
Passing RAII socket class to thread
我正在制作一个 HTTP 服务器,我想将套接字客户端 class 传递给线程来处理请求。问题是析构函数在启动线程后立即被调用,因此套接字被关闭。
代码的简化版本如下所示。
while (true) {
TcpSocket client = m_socket.accept();
std::thread clientThread([this, &client] { this->handleClient(client); });
clientThread.detach();
}
我尝试制作一个接受连接并将其传递给具有给定函数的线程的函数,但我无法让它工作。我在这里使用元组是因为你 .
template<typename Function, typename ...Args>
std::thread TcpListener::acceptAndPassToThread(Function&& function, Args&&... args)
{
int socketDescriptor = accept(m_socketDescriptor, nullptr, nullptr);
std::tuple arguments = std::make_tuple(std::forward<Args>(args)...);
std::thread clientThread([socketDescriptor, function, arguments] {
TcpSocket client = TcpSocket(socketDescriptor);
auto callArguments = std::tuple_cat(std::make_tuple(client), arguments);
std::apply(function, callArguments);
});
return clientThread;
}
我可以做这样的事情,但我不想在 class.
之外使用套接字描述符
while (true)
{
int clientDescriptor = m_socket.acceptDescriptor();
std::thread clientThread([this, clientDescriptor] {
TcpSocket client = TcpSocket::fromDescriptor(clientDescriptor);
this->handleClient(client);
});
clientThread.detach();
}
另外,使用 class 成员函数调用 TcpListener::acceptAndPassToThread 函数的正确方法是什么。
如果您可以控制 TcpSocket
class,我建议您通过将 私有实现 成员包装在 shared_ptr
(或 unique_ptr
如果你使用得当)。
要解决您眼前的问题,您可以只使用 std::shared_ptr<TcpSocket>
而不是原始的 class,如下所示:
while (true) {
std::shared_ptr<TcpSocket> client = m_socket.accept();
std::thread clientThread([this, client] { this->handleClient(client); });
clientThread.detach();
}
void handleClient(std::shared_ptr<TcpSocket> client) {
// ...
}
您必须更改函数 m_socket.accept()
才能 return 成为 shared_ptr
。
你的 RAII
class TcpSocket
真的应该 只移动 因为复制在逻辑上是错误的。当然,您可以像 std::shared_ptr
一样实现它,但是值语义有点误导。那么最好称它为反映其共同性质的东西。
要使class 仅移动,您删除 复制构造函数和 copy assignment operator 并提供两者的 move 版本。像这样:
class TcpSocket
{
public:
// ...
~TcpSocket() { this->close(); }
TcpSocket(int descriptor = -1): descriptor(descriptor) {}
// ban copying
TcpSocket(TcpSocket const&) = delete;
TcpSocket& operator=(TcpSocket const&) = delete;
// implement move so the moved from object will
// not close this descriptor
TcpSocket(TcpSocket&& other): TcpSocket()
{
std::swap(descriptor, other.descriptor);
}
// same with move assignment
// this closes any current descriptor but you may
// want to throw an exception if this descriptor
// is currently in use.
TcpSocket& operator=(TcpSocket&& other)
{
if(&other != this)
this->close(); // or throw?
std::swap(descriptor, other.descriptor);
return *this;
}
private:
void close()
{
if(descriptor != -1)
::close(descriptor);
descriptor = -1;
}
int descriptor = -1;
};
然后将套接字移动到线程中:
while (true) {
TcpSocket client = m_socket.accept();
std::thread clientThread([this, client = std::move(client)] mutable {
this->handleClient(std::move(client));
});
clientThread.detach(); // looks dangerout?
}
我正在制作一个 HTTP 服务器,我想将套接字客户端 class 传递给线程来处理请求。问题是析构函数在启动线程后立即被调用,因此套接字被关闭。 代码的简化版本如下所示。
while (true) {
TcpSocket client = m_socket.accept();
std::thread clientThread([this, &client] { this->handleClient(client); });
clientThread.detach();
}
我尝试制作一个接受连接并将其传递给具有给定函数的线程的函数,但我无法让它工作。我在这里使用元组是因为你
template<typename Function, typename ...Args>
std::thread TcpListener::acceptAndPassToThread(Function&& function, Args&&... args)
{
int socketDescriptor = accept(m_socketDescriptor, nullptr, nullptr);
std::tuple arguments = std::make_tuple(std::forward<Args>(args)...);
std::thread clientThread([socketDescriptor, function, arguments] {
TcpSocket client = TcpSocket(socketDescriptor);
auto callArguments = std::tuple_cat(std::make_tuple(client), arguments);
std::apply(function, callArguments);
});
return clientThread;
}
我可以做这样的事情,但我不想在 class.
之外使用套接字描述符while (true)
{
int clientDescriptor = m_socket.acceptDescriptor();
std::thread clientThread([this, clientDescriptor] {
TcpSocket client = TcpSocket::fromDescriptor(clientDescriptor);
this->handleClient(client);
});
clientThread.detach();
}
另外,使用 class 成员函数调用 TcpListener::acceptAndPassToThread 函数的正确方法是什么。
如果您可以控制 TcpSocket
class,我建议您通过将 私有实现 成员包装在 shared_ptr
(或 unique_ptr
如果你使用得当)。
要解决您眼前的问题,您可以只使用 std::shared_ptr<TcpSocket>
而不是原始的 class,如下所示:
while (true) {
std::shared_ptr<TcpSocket> client = m_socket.accept();
std::thread clientThread([this, client] { this->handleClient(client); });
clientThread.detach();
}
void handleClient(std::shared_ptr<TcpSocket> client) {
// ...
}
您必须更改函数 m_socket.accept()
才能 return 成为 shared_ptr
。
你的 RAII
class TcpSocket
真的应该 只移动 因为复制在逻辑上是错误的。当然,您可以像 std::shared_ptr
一样实现它,但是值语义有点误导。那么最好称它为反映其共同性质的东西。
要使class 仅移动,您删除 复制构造函数和 copy assignment operator 并提供两者的 move 版本。像这样:
class TcpSocket
{
public:
// ...
~TcpSocket() { this->close(); }
TcpSocket(int descriptor = -1): descriptor(descriptor) {}
// ban copying
TcpSocket(TcpSocket const&) = delete;
TcpSocket& operator=(TcpSocket const&) = delete;
// implement move so the moved from object will
// not close this descriptor
TcpSocket(TcpSocket&& other): TcpSocket()
{
std::swap(descriptor, other.descriptor);
}
// same with move assignment
// this closes any current descriptor but you may
// want to throw an exception if this descriptor
// is currently in use.
TcpSocket& operator=(TcpSocket&& other)
{
if(&other != this)
this->close(); // or throw?
std::swap(descriptor, other.descriptor);
return *this;
}
private:
void close()
{
if(descriptor != -1)
::close(descriptor);
descriptor = -1;
}
int descriptor = -1;
};
然后将套接字移动到线程中:
while (true) {
TcpSocket client = m_socket.accept();
std::thread clientThread([this, client = std::move(client)] mutable {
this->handleClient(std::move(client));
});
clientThread.detach(); // looks dangerout?
}