假设我有两个连接,每个连接都有各自的strand
用于线程安全。这些连接不是单独运行的,它们可以以某种方式相互通信。在此通信阶段,必须同步处理程序,以便两个处理程序不能同时修改连接对象。
所以,为了达到这个目的,我可以以嵌套的方式使用两个strand::wrap
吗?
例如,请考虑以下伪代码:
class connection /* connection actually uses shared_ptr's to ensure lifetime */
{
public:
connection *other /* somehow set */;
strand strnd /* somehow initialized correctly */;
socket sock /* somehow initialized correctly */;
streambuf buf;
int a /* shared variable */;
void trigger_read() // somewhat triggered
{
// since operations on sock are not thread-safe, use a strand to
// synchronise them
strnd.post([this] {
// now consider the following code,
async_read_until(sock, buf, '\n',
this->strnd.wrap /* wrapping by this->strnd is for sure */([](...) {
// depending on the data, this handler can alter both other
// and this
other->a ++; // not safe
this->a --; // this is safe as protected by this->strnd
}));
// can protect them both by something like,
async_read_until(sock, buf, '\n',
this->strnd.wrap(other->strnd.wrap([](...) {
// depending on the data, this handler can alter both other
// and this
other->a ++; // not safe
this->a --; // this is safe as protected by this->strnd
})));
// this???
});
}
};
答案 0 :(得分:0)
您提出的建议在Boost.Asio(1.65.1)中并没有摆脱潜在的比赛条件。考虑下面您的帖子中的摘录,
// can protect them both by something like, async_read_until(sock, buf, '\n', this->strnd.wrap(other->strnd.wrap([](...) { // depending on the data, this handler can alter both other // and this other->a ++; // not safe this->a --; // this is safe as protected by this->strnd })));
,回想一下strand::wrap
在调用时的行为与strand::dispatch
相同。如果我们在这种情况下考虑它,那么我们可以得出以下结论。但首先,我们需要使用dispatch和lambda来包装这些束(出于说明目的)。
async_read_until(sock, buf, '\n', [...](...){ // lambda1
this->strnd.dispatch([...]{ // lambda2
other->strnd.dispatch([...]{ // lambda3
other->a ++;
this->a --:
});
});
});
async_read_until
完成后,它将调用lambda1
的等效项,后者将在连接自身的子线上调用dispatch。无论是立即调用还是稍后调用,这都将导致lambda2
在安全的设置中被调用,以操纵this->a
。在lambda2
中,我们调用other->stnd.dispatch
,并通过该链lambda3
的保证将立即调用或发布到other->strnd
。无论哪种情况,lambda2
都会完成,this->strnd
所提供的并发保证也会完成。如果lambda3
在最终被调用以访问other->strnd
时被发布到this->a --
上,我们将不再为调用lambda2
提供保证。
我们还可以通过使用以下反例检查strand_service
(Boost 1.65)中的do_dispatch
函数来看到这一点,因为当函数为wrap
时,简单地在链上调用dispatch
调用。
考虑一个由strand1
和strand2
两股包裹的处理程序:
func = strand1.wrap(strand2.wrap(some_handler));
并且没有运行基础io_service
。然后,在调用func
时,由于我们当前不在该链中,因此dispatch
不会立即调用该函数,而是请求do_dispatch
执行进一步的处理。在do_dispatch
中,由于我们当前尚未在I / O服务中运行,但是没有其他处理程序具有该链的锁,因此do_dispatch
不会发出立即调用的信号,而是将处理程序推入就绪队列以进行处理。 strand1
。一旦进入就绪队列,处理完就绪队列后,便会简单地调用该处理程序(请参见do_complete
)。
这意味着调用strand2
的包装器以在strand2
,strand1
上分派的点完全是通过提供保证来完成的。如果strand2
的调度调用导致立即调用,那么我们很好,如果发生冲突,并且必须将处理程序推入strand2的等待队列中,则无法确定何时将最终调用该处理程序。>
总而言之,将处理程序包装成多股并不能保证该处理程序将处于具有所有股并发保证的环境中。