Boost.asio网络库多线程并发处理实现,以及asio在多线程模型中线程的调度情况和线程安全

1、实现多线程方法:

其实就是多个线程同时调用io_service::run

   

for(int i = 0; i < iThreads; i++){//创建线程,函数体是CIoService::run
    CThreadPtr oThreadPtr(new CThread(boost::bind(&CIoService::run, &oIoSrv)));
    vecThreads.push_back(oThreadPtr);
}

2、多线程调度情况:

asio规定:只能在调用io_service::run的线程中才能调用事件完成处理器。

注:事件完成处理器就是你async_accept、async_write等注册的句柄,类似于回调的东西。

单线程:

如果只有一个线程调用io_service::run,根据asio的规定,事件完成处理器也只能在这个线程中执行。也就是说,你所有代码都在同一个线程中运行,因此变量的访问是安全的。

多线程:

如果有多个线程同时调用io_service::run以实现多线程并发处理。对于asio来说,这些线程都是平等的,没有主次之分。如果你投递的一个请求比如async_write完成时,asio将随机的激活调用io_service::run的线程。并在这个线程中调用事件完成处理器(async_write当时注册的句柄)。如果你的代码耗时较长,这个时候你投递的另一个async_write请求完成时,asio将不等待你的代码处理完成,它将在另外的一个调用io_service::run线程中,调用async_write当时注册的句柄。也就是说,你注册的事件完成处理器有可能同时在多个线程中调用。

当然你可以使用 boost::asio::io_service::strand让完成事件处理器的调用【当处理函数不是线程安全的,强烈建议使用这种方式】,在同一时间只有一个, 比如下面的的代码:

  socket_.async_read_some(boost::asio::buffer(buffer_),
      strand_.wrap(
        boost::bind(&connection::handle_read, shared_from_this(),
          boost::asio::placeholders::error,
          boost::asio::placeholders::bytes_transferred)));

...

boost::asio::io_service::strand strand_;

 

此时async_read_some完成后调用handle_read时,必须等待其它handle_read调用完成时才能被执行(async_read_some引起的handle_read调用)。

      多线程调用时,还有一个重要的问题,那就是无序化。比如说,你短时间内投递多个async_write,那么完成处理器的调用并不是按照你投递async_write的顺序调用的。asio第一次调用完成事件处理器,有可能是第二次async_write返回的结果,也有可能是第3次的。使用strand也是这样的。strand只是保证同一时间只运行一个完成处理器,但它并不保证顺序。


代码测试:

服务器:

将下面的代码编译以后,使用cmd命令提示符下传人参数 调用

比如:BoostPro.exe 0.0.0.0 4321 3  

客服端 使用windows自带的telnet

cmd命令提示符:

telnet 127.0.0.1 4321

 

原理:客户端连接成功后,同一时间调用20次boost::asio::async_write给客户端发送数据,并且在完成事件处理器中打印调用序号,和线程ID。


测试代码实现如下:

#include 
#include 
using namespace std;

#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace boost;



typedef boost::shared_ptr CStrPtr;
typedef boost::thread CThread;
typedef boost::shared_ptr CThreadPtr;
class CTcpConn;
typedef boost::shared_ptr CTcpConnPtr;

typedef boost::mutex CMutex;
typedef boost::mutex::scoped_lock CScopedLock;
typedef boost::asio::io_service CIoService;
typedef boost::asio::ip::tcp::socket CSocket;
typedef boost::asio::ip::tcp::acceptor CAcceptor;

typedef boost::asio::ip::tcp::resolver CResolver;
typedef boost::asio::ip::tcp::resolver::query CQuery;
typedef boost::asio::ip::tcp::resolver::iterator CIterator;
typedef boost::asio::ip::tcp::endpoint CEndPt;


class CTcpConn : public boost::enable_shared_from_this{
private:
	CMutex oMutex;
	CSocket oSocket;
public:
	CTcpConn(CIoService &iosrv) : oSocket(iosrv){}
	static CTcpConnPtr CreateTcpConn(CIoService& iosrv){return CTcpConnPtr(new CTcpConn(iosrv));}
	void Start(){
		for(int i = 0; i < 20; i++){
			CStrPtr oStrPtr(new string);
			*oStrPtr = boost::lexical_cast(boost::this_thread::get_id()); *oStrPtr += "\r\n";
			boost::asio::async_write(oSocket,boost::asio::buffer(*oStrPtr),
				boost::bind(&CTcpConn::WriteHandler, shared_from_this(), 
				boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred, oStrPtr, i));
		}
	}
	CSocket& GetSocket(){return oSocket;}
private:
	void WriteHandler(const boost::system::error_code& errorcode, std::size_t transferredbytes, CStrPtr oStrPtr, int index){
		CScopedLock oScopedLock(oMutex);
		if(!errorcode)			cout << "SendSeq : " << index << ", ThreadId : " << boost::this_thread::get_id() << endl;
		else								cout << "Connections Unavailable !" << endl;
	}
};

class CServer : private boost::noncopyable{
private:
	CIoService oIoSrv;
	CAcceptor oAcceptor;
	std::size_t iThreads;
	std::vector vecThreads;
public:
	CServer(string& ip, string& port, int threads) : oAcceptor(oIoSrv), iThreads(threads){
		CResolver oResolver(oIoSrv);
		CQuery oQuery(ip, port);
		CIterator oIterator = oResolver.resolve(oQuery);
		CEndPt oEndPt = *oResolver.resolve(oQuery);

		//打开、选项、绑定并监听
		oAcceptor.open(oEndPt.protocol());
		oAcceptor.set_option(CAcceptor::reuse_address(true));
		oAcceptor.bind(oEndPt);
		oAcceptor.listen();

		//异步监听
		StartAccepting();
	}
	~CServer(){Stop();}
	void StartAccepting(){//接收来自客户端【telnet】的TCP连接
		CTcpConnPtr oTcpConnPtr = CTcpConn::CreateTcpConn(oAcceptor.get_io_service());
		oAcceptor.async_accept(oTcpConnPtr->GetSocket(), boost::bind(&CServer::AcceptHandler, this, 
			boost::asio::placeholders::error, oTcpConnPtr));
	}
	void AcceptHandler(const boost::system::error_code& errorcode, CTcpConnPtr oNewTcpConnPtr){
		if(!errorcode)		oNewTcpConnPtr->Start();
		StartAccepting();
	}
	void Start(){
		for(int i = 0; i < iThreads; i++){//创建线程,函数体是CIoService::run
			CThreadPtr oThreadPtr(new CThread(boost::bind(&CIoService::run, &oIoSrv)));
			vecThreads.push_back(oThreadPtr);
		}
	}
	void Stop(){
		oIoSrv.stop();//停止服务;等待所有线程结束
		for(std::vector::iterator it = vecThreads.begin(); it != vecThreads.end(); it++){
			(*it)->join();
		}
	}
};


int main(int argc, char* argv[]){
	if(argc != 4){
		cout << "Usage : ip port threads" << endl;
		return 1;
	}
	string sIp(argv[1]), sPort(argv[2]);
	int iThreads = boost::lexical_cast(argv[3]);
	try{
		CServer oServer(sIp, sPort, iThreads);
		cout << "StartUp Service" << endl;
		oServer.Start();
		getchar();//按回车键退出程序
		oServer.Stop();
		cout << "ShutDown Service" << endl;
	}
	catch(std::exception& e){
		cout << "Exception : " << e.what() << endl;
	}
	return 0;
}

运行效果如下:

Boost.asio网络库多线程并发处理实现,以及asio在多线程模型中线程的调度情况和线程安全_第1张图片

你可能感兴趣的:(Boost(开放的源码,强大的工具))