【发布时间】:2012-02-18 07:38:55
【问题描述】:
我正在使用boost::asio(用于套接字)、boost::thread(用于线程)、libconfig++(用于读取配置文件)和protocol buffers(用于协议实现)编写一个多线程服务器。
服务器或多或少遵循这条路线:main() -> 创建一个应用程序对象 -> 运行应用程序对象。应用程序加载配置文件,然后创建服务器对象(作为 const 传递给配置类)。服务器对象配置自己并绑定端口,开始接受,等等。每当检测到新客户端时,服务器都会创建一个新的客户端对象,然后创建一个运行客户端连接处理程序的线程。
所有这些都是为了说明配置文件是从我的 Application 类加载的,然后一直传递到我的 Client 类。如果 libconfig 对象一直直接传递给客户端,这应该不会造成任何麻烦,但众所周知,多线程意味着当两个或多个线程同时访问时内存会损坏。
The way to solve this was discussed in other post 最终实现了一个自动解决互斥问题的包装器。
魔法课
app_config.h
#ifndef _APP_CONFIG_H_
#define _APP_CONFIG_H_ 1
#include <boost/shared_ptr.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/locks.hpp>
#include <boost/noncopyable.hpp>
#include <libconfig.h++>
#include <string>
namespace BBCP {
namespace App {
class ConfigLock;
class Config {
public:
friend class BBCP::App::ConfigLock;
Config(std::string const &file) :
cfg(new libconfig::Config()),
mutex(new boost::mutex())
{
cfg->readFile(file.c_str());
}
private:
boost::shared_ptr<libconfig::Config> cfg;
boost::shared_ptr<boost::mutex> mutex;
};
class Server;
class Client;
class ConfigLock : boost::noncopyable {
public:
ConfigLock(BBCP::App::Config const &wrapper) :
cfg(wrapper.cfg),
mutex(wrapper.mutex),
lock(new LockType(*mutex))
{ }
libconfig::Config &get() throw() { return *cfg; };
private:
boost::shared_ptr<libconfig::Config> cfg;
boost::shared_ptr<boost::mutex> mutex;
typedef boost::lock_guard<boost::mutex> LockType;
boost::shared_ptr<LockType> lock;
};
}
}
#endif
对于懒惰的人来说,这个类包括......好吧,两个类(讽刺?):BBCP::App::Config 和 BBCP::App::ConfigLock。 BBCP::App::Config 只是加载一个文件,而BBCP::App::ConfigLock 将BBCP::App::Config 作为参数,然后锁定BBCP::App::Config 的互斥锁。创建锁后,调用BBCP::App::ConfigLock::get,它返回对libconfig::Config 对象的引用!。
问题
嗯:
server.cpp:
void BBCP::App::Server::startAccept() {
newClient.reset(new BBCP::App::Client(io_service, config_wrapper));
acceptor.async_accept(newClient->getSocket(), boost::bind(&BBCP::App::Server::acceptHandler, this, boost::asio::placeholders::error));
}
这个函数创建一个新的客户端对象,加载了boost::asio::io_service对象和BBCP::App::Config对象。
服务器.cpp
void BBCP::App::Server::acceptHandler(boost::system::error_code const &e) {
if (!acceptor.is_open()) {
// ARR ERROR!
return;
}
if (!e) {
client_pool.create_thread(*newClient);
}
else {
// HANDLE ME ERROR
throw;
}
startAccept();
}
此函数创建一个新线程或(尚未实现)错误以防……嗯,错误,然后再次启动接受循环。
在这部分之前,客户端代码大多无关紧要:
client.cpp:
void BBCP::App::Client::parseBody() {
BBCP::Protocol::Header header;
BBCP::Protocol::Hello hello;
boost::scoped_ptr<BBCP::App::ConfigLock> lock;
libconfig::Config *cfg;
(...)
switch ((enum BBCP::Protocol::PacketType)header.type()) {
case BBCP::Protocol::HELLO:
(...)
// config_wrapper is a private variable in the client class!
lock.reset(new BBCP::App::ConfigLock(config_wrapper));
// ARRRRRRR HERE BE DRAGOONS!!
*cfg = lock->get();
(...)
lock.reset();
break;
(...)
}
(...)
}
好吧,说实话,我没想到会出现这种错误:
/usr/include/libconfig.h++: In member function ‘void BBCP::App::Client::parseBody()’:
/usr/include/libconfig.h++:338:13: error: ‘libconfig::Config& libconfig::Config::operator=(const libconfig::Config&)’ is private
client.cpp:64:30: error: within this context
client.cpp:71:21: error: request for member ‘exists’ in ‘cfg’, which is of non-class type ‘libconfig::Config*’
client.cpp:77:51: error: request for member ‘lookup’ in ‘cfg’, which is of non-class type ‘libconfig::Config*’
但就是这样,我需要一些方法来解决它 :(。我尝试将 BBCP::App::Client 设为 BBCP::App::ConfigLock 的朋友类,但结果如下:
In file included from ../include/app_config.h:4:0,
from ../include/app_main.h:6,
from main.cpp:18:
../include/app_client.h:15:53: error: ‘BBCP::App::Config’ has not been declared
In file included from ../include/app_config.h:4:0,
from ../include/app_main.h:6,
from main.cpp:18:
../include/app_client.h:32:5: error: ‘Config’ in namespace ‘BBCP::App’ does not name a type
In file included from ../include/app_config.h:4:0,
from ../include/app_main.h:6,
from main.cpp:18:
../include/app_client.h: In constructor ‘BBCP::App::Client::Client(boost::asio::io_service&, const int&)’:
../include/app_client.h:15:120: error: class ‘BBCP::App::Client’ does not have any field named ‘config_wrapper’
然后我像O_O一样去了,所以我放弃了,来到这里,再次寻求一些über C++ guru hackz0r的帮助,并因为试图访问另一个类的私有成员这样的错误行为而受到责骂。
【问题讨论】:
-
我不明白这部分:“我已经尝试让
BBCP::App::Client成为BBCP::App::ConfigLock的朋友类”。错误消息是抱怨BBCP::App::Client成员函数试图访问libconfig::Config的私有方法(重载赋值运算符),所以为了使用朋友类方法,您不需要将BBCP::App::Client设为libconfig::Config的朋友班? -
小建议,重命名
_APP_CONFIG_H_。 §17.4.3.2.1 说Each name that contains a double underscore (_ _) or begins with an underscore followed by an uppercase letter (2.11) is reserved to the implementation for any use. -
多线程只读访问变量没有问题。我不知道您是否打算在程序运行时修改配置对象,但如果您不这样做,一切都会变得更简单。此外,您应该花一些时间进行设计(为什么配置对象不能公开复制)并了解编译器试图在错误消息中告诉您的内容,因为它们可以帮助您找出问题所在。
-
@ruakh:是的,对不起,我看错了。
-
@JulianBayardo:检查Where do I find the current C or C++ standard documents?。
标签: c++ multithreading boost boost-asio boost-thread