正如标题所说我有一个关于以下场景的问题(简单的例子):
假设我有一个下面的Generator-Class对象,它不断更新其dataChunk成员(在主线程中运行)。
class Generator
{
void generateData();
uint8_t dataChunk[999];
}
此外我有一个异步。 1-N客户端可以连接到的TCP连接的接受者(在第二个线程中运行)。 接受者为每个新的客户端连接启动一个新线程,其中下面的Connection类的一个对象从客户端接收请求消息,并提供一小部分dataChunk(属于Generator)作为答案。然后等待新的请求等等......
class Connection
{
void setDataChunk(uint8_t* dataChunk);
void handleRequest();
uint8_t* dataChunk;
}
最后一个实际问题:所需的行为是Generator对象生成一个新的dataChunk,并等待所有1-N Connection对象与其客户端请求交叉,直到它生成一个新的dataChunk。
当Connection对象处理其请求时,如何锁定dataChunk以写入Generator对象的访问权限,但是在它们各自的线程中的所有Connection对象在其请求处理阶段期间应该同时具有读取访问权限
另一方面,Connection对象在处理各自的请求后应该等待新的dataChunk,而不会丢弃新的客户端请求。
- >我认为一个互斥锁不会在这里做到这一点。
我的第一个想法是在对象之间共享一个结构,其中包含Generator的信号量和连接的信号量向量。有了这些,每个对象都可以理解"全系统的状态并相应地工作。
你们怎么想?在这种情况下,最佳做法是什么?
提前致谢!
答案 0 :(得分:1)
有几种方法可以解决它。
您可以使用std::shared_mutex
。
void Connection::handleRequest()
{
while(true)
{
std::shared_lock<std::shared_mutex> lock(GeneratorObj.shared_mutex);
if(GeneratorObj.DataIsAvailable()) // we need to know that data is available
{
// Send to client
break;
}
}
}
void Generator::generateData()
{
std::unique_lock<std::shared_mutex> lock(GeneratorObj.shared_mutex);
// Generate data
}
或者您可以使用boost::lockfree::queue
,但数据结构会有所不同。
答案 1 :(得分:0)
当Connection对象处理其请求时,如何锁定dataChunk以写入Generator对象的访问权限,但是在它们各自的线程中的所有Connection对象在其请求处理阶段期间应该同时具有读取访问权限
我会制作一个逻辑链操作,包括生成。
以下是一个示例:
deadline_timer
对象来发出屏障信号。这样可以方便地将generateData
调用放入异步调用链中。<强> Live On Coliru 强>
#include <boost/asio.hpp>
#include <list>
#include <iostream>
namespace ba = boost::asio;
using ba::ip::tcp;
using boost::system::error_code;
using Clock = std::chrono::high_resolution_clock;
using Duration = Clock::duration;
using namespace std::chrono_literals;
struct Generator {
void generateData();
uint8_t dataChunk[999];
};
struct Server {
Server(unsigned short port) : _port(port) {
_barrier.expires_at(boost::posix_time::neg_infin);
_acc.set_option(tcp::acceptor::reuse_address());
accept_loop();
}
void generate_loop() {
assert(n_sending == 0);
garbage_collect(); // remove dead connections, don't interfere with sending
if (_socks.empty()) {
std::clog << "No more connections; pausing Generator\n";
} else {
_gen.generateData();
_barrier.expires_at(boost::posix_time::pos_infin);
for (auto& s : _socks) {
++n_sending;
ba::async_write(s, ba::buffer(_gen.dataChunk), [this,&s](error_code ec, size_t written) {
assert(n_sending);
--n_sending; // even if failed, decreases pending operation
if (ec) {
std::cerr << "Write: " << ec.message() << "\n";
s.close();
}
std::clog << "Written: " << written << ", " << n_sending << " to go\n";
if (!n_sending) {
// green light to generate next chunk
_barrier.expires_at(boost::posix_time::neg_infin);
}
});
}
_barrier.async_wait([this](error_code ec) {
if (ec && ec != ba::error::operation_aborted)
std::cerr << "Client activity: " << ec.message() << "\n";
else generate_loop();
});
}
}
void accept_loop() {
_acc.async_accept(_accepting, [this](error_code ec) {
if (ec) {
std::cerr << "Accept fail: " << ec.message() << "\n";
} else {
std::clog << "Accepted: " << _accepting.remote_endpoint() << "\n";
_socks.push_back(std::move(_accepting));
if (_socks.size() == 1) // first connection?
generate_loop(); // start generator
accept_loop();
}
});
}
void run_for(Duration d) {
_svc.run_for(d);
}
void garbage_collect() {
_socks.remove_if([](tcp::socket& s) { return !s.is_open(); });
}
private:
ba::io_service _svc;
unsigned short _port;
tcp::acceptor _acc { _svc, { {}, _port } };
tcp::socket _accepting {_svc};
std::list<tcp::socket> _socks;
Generator _gen;
size_t n_sending = 0;
ba::deadline_timer _barrier {_svc};
};
int main() {
Server s(6767);
s.run_for(3s); // COLIRU
}
#include <fstream>
// synchronously generate random data chunks
void Generator::generateData() {
std::ifstream ifs("/dev/urandom", std::ios::binary);
ifs.read(reinterpret_cast<char*>(dataChunk), sizeof(dataChunk));
std::clog << "Generated chunk: " << ifs.gcount() << "\n";
}
打印(仅适用于1个客户):
Accepted: 127.0.0.1:60870
Generated chunk: 999
Written: 999, 0 to go
Generated chunk: 999
[... snip ~4000 lines ...]
Written: 999, 0 to go
Generated chunk: 999
Write: Broken pipe
Written: 0, 0 to go
No more connections; pausing Generator