int main() { boost::asio::io_service io_svc; std::vector<std::thread> vecThread; for (int i = 0; i < 5; ++i) { vecThread.emplace_back(std::thread([](){ io_svc.run(); })); } return 0; }
如果按上面的代码将一个io_svc 分配多个线程按并发处理,处理的对象是TCP 服务时。发现对同一个TCP 客户端的连接的处理会在多个线程中。
如果是这样的话,那这一个客户端发送过来数据被分成多段后,在服务端会不会乱,而这样我是不是需要加锁控制,可如果加锁的话又应该怎么加呢?
按时我的想法是,如果 是同一个话,它如果只在同一个线程中那么这个我就不用担心了,因为它会顺序到达,那么我收到的头部和数据部就不会被分到两个线程中去了。
这主要问题是TCP 服务端使用异步的方式接收客户端的数据,首先收到固定的头部数据,然后解析头部得到数据部的长度,然后再次异步接收实际的数据。如果这两个步骤被分到了两个线程中去了。导致了这两个部分发生了并发操作,那就尴尬了。
单个线程用于数据接收,放到一个队列,开多个消费线程用于数据处理
每一次接收都有一个处理函数,这个处理函数是自己提供的。而这个处理函数提供了之后,如果没有再次添加异步读的话,那么这个连接将不会继续到TCP 上去读取数据,这个连接的TCP 上的数据将会被存起来,直到你调用异步或者同步读。
而这里的正确处理方式应该是:在每次读的处理函数结束之后,再去读TCP 上面的数据,这样一个连接就会一直按顺序去TCP 上读取数据,而不需要加锁。当然了,如果你的处理函数中先添加了异步读,再添加了数据处理代码,那就有可能会有问题。在这个时候就可能存在,你在处理数据的时候,异步读调用了新的处理函数。那么这种代码是不可取的,所以正确的处理应该是将读到的数据处理完成之后,再添加下一个异步读。而不是像你说的那个再多开几个消费线程,这样在逻辑上就会比较乱,同时多开了线程必然导致锁的增加,也必然存在锁的消耗。