我知道这个问题之前曾经多次被问过,但他们都专注于strand
和那种东西。让我们以更一般的方式看一下它。
首先要做的是 - 让我们来看看documentation:
线程安全
不同的物体:安全。
共享对象:不安全。
所以对我来说,这些函数都不能与另一个函数同时调用,即使它们都是run
通过一个单个中的相同io_service
对象进行调用螺纹:
// thread 1
sock.async_write(/* ... */); // happens at the same time as async_read from the thread 2
// thread 2
sock.async_read(/* ... */); // happens at the same time as async_write from the thread 1
// thread 3 (the only thread that calls io_service::run function)
while (true)
{
io_service.run();
}
我是这么认为的,因为在对应链接中没有措辞表明它是“安全的,如果这些操作在隐式或显式链下运行”。
我是对的吗?
答案 0 :(得分:0)
在提供的示例中,thread1
和thread2
违反了套接字的线程安全要求。 thread3
无关紧要。
类型的线程安全性和io_service
的运行是正交的。 Thread and Boost.Asio文档说明:
通常,并行使用不同的对象是安全的,但同时使用单个对象是不安全的。但是,
io_service
等类型提供了更强的保证,即同时使用单个对象是安全的。
因此,对于类型T
:
t1
的离散对象t2
和T
,则thread1
不能同时调用函数t1
thread2
正在调用t2
t1
和t2
类型T
的离散对象,thread1
同时安全在t1
调用thread2
t2
上的函数
t
类型的对象T
,thread1
t
时thread2
不能同时调用t
上的函数}正在调用t
T
类型的对象thread1
时,t
同时调用thread2
上的函数是安全的{ {1}}正在调用t
组合操作(例如async_read()
)对线程安全性的影响可能不明显。组合操作在对I / O对象的零次或多次调用中实现。启动功能可以尝试执行I / O操作,并且在运行io_service
的线程的上下文内启动任何其他中间操作。因此,给定ip::tcp::socket
共享对象的线程安全性是不安全的:
thread1 | thread2
--------------------------------------+-------------------------------------------
ip::tcp::socket socket(io_service); | io_service::work work(io_service);
... | io_service.run();
async_read(socket, ...); | |-- ...
socket.async_write_some(...) | `-- ... // possible socket.async_receive()
上述内容不安全,因为thread1
可能会在socket.async_write_some()
内的中间操作调用thread2
时尝试调用socket.async_receive()
。
答案 1 :(得分:0)
你是否真的使用std :: thread构造函数在某处明确地启动了线程?我要求确定您是否对async_read
和async_write
函数实际执行的内容感到困惑。
如果你 - 根据你的例子 - 做到了这一点:
sock.async_write(write_handler);
sock.async_read(read_handler);
// Notice: no need to put this in a while loop, this is a blocking call.
io_service.run();
然后您不需要关心多线程,因为两个处理程序都应该内部 io_service.run()
函数依次执行(只有它们的执行顺序是未定义的。)
现在,如果你有这样的事情:
sock.async_write(write_handler);
sock.async_read(read_handler);
std::thread thread1([&]() { io_service.run(); });
std::thread thread2([&]() { io_service.run(); });
这仍然有效,但现在你无法告诉执行处理程序的io_service.run()
函数。可以同时调用两个处理程序同时(即可以在另一个处理程序完成之前启动)。在这种情况下,考虑绞线或显式锁定是明智的。
然后是第三种情况:
std::thread thread1([&]() { sock.async_write(write_handler); });
std::thread thread2([&]() { sock.async_read(read_handler); });
io_service.run();
我相信这就是@TannerSansbury对你的伪代码的理解,正如他在回答中所解释的那样,这种做法是错误的。
这三者中哪一个与您所拥有的最相似?