我可以同时调用任何套接字函数,即使它们都在一个单独的线程中运行

时间:2016-08-03 18:01:27

标签: c++ multithreading boost boost-asio

我知道这个问题之前曾经多次被问过,但他们都专注于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();
}

我是这么认为的,因为在对应链接中没有措辞表明它是“安全的,如果这些操作在隐式或显式链下运行”。

我是对的吗?

2 个答案:

答案 0 :(得分:0)

在提供的示例中,thread1thread2违反了套接字的线程安全要求。 thread3无关紧要。

类型的线程安全性和io_service的运行是正交的。 Thread and Boost.Asio文档说明:

  

通常,并行使用不同的对象是安全的,但同时使用单个对象是不安全的。但是,io_service等类型提供了更强的保证,即同时使用单个对象是安全的。

因此,对于类型T

  • 当不同对象的线程安全性不安全时,给定两个类型为t1的离散对象t2T,则thread1不能同时调用函数t1 thread2正在调用t2
  • 上的函数
  • 当不同对象的线程安全性是安全的时,给定两个t1t2类型T的离散对象,thread1同时安全在t1调用thread2
  • 上的函数时调用t2上的函数
  • 当共享对象的线程安全性不安全时,给定t类型的对象Tthread1 tthread2不能同时调用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_readasync_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对你的伪代码的理解,正如他在回答中所解释的那样,这种做法是错误的。

这三者中哪一个与您所拥有的最相似?