1 介绍
在C++中,std::thread对象的生命周期与它所代表的线程的生命周期是紧密关联的,但它们并不完全相同。
首先,当一个std::thread对象被创建时,它所代表的线程会立即开始执行。这一点体现了C++中即时执行的设计选择,旨在简化线程的创建和管理。这种设计减少了开发者的认知负荷,因为不需要额外的启动调用来开始线程的执行。
其次,std::thread对象遵循RAII(资源获取即初始化)原则,这意味着当std::thread对象被销毁时,它所代表的线程也会被终止。如果线程在std::thread对象销毁时仍在运行,那么程序的行为将是未定义的。因此,通常需要在线程对象的生命周期结束前,通过调用成员函数join或detach来适当地结束线程。
再者,std::thread对象是不可复制的,只具有移动属性。这是因为每个线程都有一个唯一的标识符,即线程ID,而复制一个std::thread对象将导致两个线程对象拥有同一个线程ID,这是不可能的。因此,std::thread对象只能通过移动构造函数或移动赋值操作符来转移所有权。
调用了detach()
方法将该线程分离,使其在后台运行并自行结束。由于线程被分离后,其生命周期与线程对象的生命周期不再相关联,因此即使线程对象被销毁,线程本身仍然可以继续执行直到完成。
最后,需要注意的是,即使线程已经分离(detached)或者加入了(joined),std::thread对象的析构函数仍然会检查线程是否还在运行。如果是,它将调用std::terminate()来终止程序,除非线程已经被分离或者加入了。这是为了确保不会出现悬挂线程(orphan threads),即那些在std::thread对象销毁后仍在运行的线程。
综上所述,std::thread对象的生命周期通常决定了它所代表的线程的开始和结束,但它们并不是完全同步的。正确管理std::thread对象的生命周期对于避免程序错误和资源泄露至关重要。
2 示例
以下是使用Boost.Beast实现的WebSocket客户端和服务器的示例程序:
WebSocket客户端示例代码:
#include <boost/beast/core.hpp>
#include <boost/beast/websocket.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <cstdlib>
#include <iostream>
#include <string>
namespace beast = boost::beast;
namespace websocket = beast::websocket;
namespace net = boost::asio;
using tcp = net::ip::tcp;
int main(int argc, char* argv[]) {
try {
if (argc != 4) {
std::cerr << "Usage: ws_client <host> <port> <message>" << std::endl;
return EXIT_FAILURE;
}
auto const host = argv[1];
auto const port = argv[2];
auto const message = argv[3];
net::io_context ioc;
tcp::resolver resolver{ioc};
websocket::stream<tcp::socket> ws{ioc};
auto const results = resolver.resolve(host, port);
net::connect(ws.next_layer(), results.begin(), results.end());
ws.handshake(host, "/");
ws.write(net::buffer(message));
beast::flat_buffer buffer;
ws.read(buffer);
ws.close(websocket::close_code::normal);
std::cout << "Received response: " << beast::make_printable(buffer.data()) << std::endl;
} catch (std::exception const& e) {
std::cerr << "Error: " << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
WebSocket服务器示例代码:
#include <boost/beast/core.hpp>
#include <boost/beast/websocket.hpp>
#include <boost/asio/bind_executor.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/strand.hpp>
#include <boost/config.hpp>
#include <cstdlib>
#include <iostream>
#include <memory>
#include <string>
#include <thread>
#include <vector>
namespace beast = boost::beast;
namespace websocket = beast::websocket;
namespace net = boost::asio;
using tcp = net::ip::tcp;
void session(tcp::socket socket) {
bool close = false;
beast::error_code ec;
websocket::stream<tcp::socket> ws{std::move(socket)};
ws.accept(ec);
if (ec) {
std::cerr << "Error accepting connection: " << ec.message() << std::endl;
return;
}
beast::flat_buffer buffer;
ws.read(buffer);
std::string message = beast::make_printable(buffer.data());
std::cout << "Received message: " << message << std::endl;
std::string response = "Hello, World!";
ws.write(net::buffer(response), ec);
if (ec) {
std::cerr << "Error writing response: " << ec.message() << std::endl;
return;
}
close = true;
if (close) {
ws.close(websocket::close_code::normal, ec);
}
}
int main(int argc, char* argv[]) {
try {
if (argc != 3) {
std::cerr << "Usage: ws_server <address> <port>" << std::endl;
return EXIT_FAILURE;
}
auto const address = argv[1];
auto const port = argv[2];
net::io_context ioc{1};
tcp::acceptor acceptor{ioc, {address, port}};
tcp::socket socket{ioc};
while (true) {
acceptor.accept(socket, net::use_future);
std::make_shared<std::thread>(std::bind(&session, std::move(socket)))->detach();
}
} catch (std::exception const& e) {
std::cerr << "Error: " << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}