Этот вопрос, скорее всего, является repost: Что {фигурные скобки} вокруг имени переменной javascript означают
Но в качестве ответа это назначение деструктуризации , Если ваш объект передается в зеркалах, на которые ссылается переменная, вы можете получить это конкретное поле во время назначения.
Я провел обширные тесты и не смог сломать asio. Даже без блокировки каких-либо мьютексов.
Тем не менее я бы посоветовал вам использовать async_read
и async_write
с мьютексом вокруг каждого из этих вызовов.
Я считаю, что единственный обратный путь что ваши обработчики завершения могут быть вызваны одновременно, если у вас есть более одного потока, вызывающего io_service::run
.
В моем случае это не было проблемой. Вот мой тестовый код:
#include <boost/thread.hpp>
#include <boost/date_time.hpp>
#include <boost/asio.hpp>
#include <vector>
using namespace std;
char databuffer[256];
vector<boost::asio::const_buffer> scatter_buffer;
boost::mutex my_test_mutex;
void my_test_func(boost::asio::ip::tcp::socket* socket, boost::asio::io_service *io) {
while(1) {
boost::this_thread::sleep(boost::posix_time::microsec(rand()%1000));
//my_test_mutex.lock(); // It would be safer
socket->async_send(scatter_buffer, boost::bind(&mycallback));
//my_test_mutex.unlock(); // It would be safer
}
}
int main(int argc, char **argv) {
for(int i = 0; i < 256; ++i)
databuffer[i] = i;
for(int i = 0; i < 4*90; ++i)
scatter_buffer.push_back(boost::asio::buffer(databuffer));
boost::asio::io_service my_test_ioservice;
boost::asio::ip::tcp::socket my_test_socket(my_test_ioservice);
boost::asio::ip::tcp::resolver my_test_tcp_resolver(my_test_ioservice);
boost::asio::ip::tcp::resolver::query my_test_tcp_query("192.168.1.10", "40000");
boost::asio::ip::tcp::resolver::iterator my_test_tcp_iterator = my_test_tcp_resolver.resolve(my_test_tcp_query);
boost::asio::connect(my_test_socket, my_test_tcp_iterator);
for (size_t i = 0; i < 8; ++i) {
boost::shared_ptr<boost::thread> thread(
new boost::thread(my_test_func, &my_test_socket, &my_test_ioservice));
}
while(1) {
my_test_ioservice.run_one();
boost::this_thread::sleep(boost::posix_time::microsec(rand()%1000));
}
return 0;
}
И вот мой временный сервер в python:
import socket
def main():
mysocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
mysocket.bind((socket.gethostname(), 40000))
mysocket.listen(1)
while 1:
(clientsocket, address) = mysocket.accept()
print("Connection from: " + str(address))
i = 0
count = 0
while i == ord(clientsocket.recv(1)):
i += 1
i %= 256
count+=1
if count % 1000 == 0:
print(count/1000)
print("Error!")
return 0
if __name__ == '__main__':
main()
Обратите внимание, что запуск этого кода может заставьте ваш компьютер трэш.
Это зависит от доступа к одному объекту сокета из нескольких потоков. Предположим, что у вас есть два потока, работающих с такой же функцией io_service::run()
.
Если, например, вы одновременно выполняете чтение и запись или можете выполнять операцию отмены из другого потока. Тогда это небезопасно.
Однако, если ваш протокол выполняет только одну операцию за раз.
io_service::run
, и вы пытаетесь сделать операции одновременно - скажем, отменить и прочитать операцию, тогда вы должны использовать пряди. В документации Boost.Asio есть учебное пособие. Похоже, этот вопрос сводится к следующему:
, что происходит, когда
blockquote>async_write_some()
вызывается одновременно в одном сокете из двух разных потоковЯ считаю, что это точно операция, которая не является потокобезопасной. Порядок, в котором эти буферы выходят на провод, не определен, и они могут быть чередующимися. Особенно, если вы используете функцию удобства
async_write()
, так как она реализована как серия вызововasync_write_some()
внизу, пока не будет отправлен весь буфер. В этом случае каждый фрагмент, который отправляется из двух потоков, может чередоваться случайным образом.Единственный способ защитить вас от удара этого случая - это построить вашу программу, чтобы избежать подобных ситуаций.
Один из способов сделать это - написать буфер отправки прикладного уровня, который один поток отвечает за нажатие на сокет. Таким образом, вы можете защитить только буфер отправки. Имейте в виду, что простой
std::vector
не будет работать, так как добавление байтов в конец может привести к перераспределению его, возможно, пока есть выдающийсяasync_write_some()
, ссылающийся на него. Вместо этого, вероятно, неплохо использовать связанный список буферов и использовать функцию разброса / сбора asio.
В соответствии с новатором 2008 года повышают 1,37 обновления asio, некоторые синхронные операции, в том числе записи «теперь потокобезопасны», позволяющие «одновременные синхронные операции в отдельном сокете, если они поддерживаются ОС» boost 1.37.0 history . Казалось бы, это поддерживает то, что вы видите, но упрощение предложения «Общие объекты: небезопасное» остается в файлах boost для ip :: tcp :: socket.
Используйте boost::asio::io_service::strand
для асинхронных обработчиков, которые не являются потокобезопасными.
Строка определяется как строго последовательный вызов обработчиков событий (то есть без одновременных призывание). Использование нитей позволяет выполнять код в многопоточной программе без необходимости явной блокировки (например, с помощью мьютексов).
Учебник timer , вероятно, самый простой способ оберните голову вокруг нитей.
io_service
, это совершенно другая проблема.
– Sam Miller
9 September 2011 в 17:19
Ключом к пониманию ASIO является понимание того, что обработчики завершения выполняются только в контексте потока, который вызвал io_service.run()
, независимо от того, какой поток называется асинхронным методом. Если вы только вызвали io_service.run()
в одном потоке, то все обработчики завершения будут выполняться последовательно в контексте этого потока. Если вы вызвали io_service.run()
в более чем одном потоке, то обработчики завершения будут выполняться в контексте одного из этих потоков. Вы можете думать об этом как о пуле потоков, где потоки в пуле - это те потоки, которые вызвали io_service.run()
на одном и том же объекте io_service
.
Если у вас есть несколько потоков, вызовите io_service.run()
, тогда вы может заставить сериализовать обработчики завершения, помещая их в strand
.
Чтобы ответить на последнюю часть вашего вопроса, вы должны позвонить boost::async_write()
. Это отправит операцию записи в поток, который вызвал io_service.run()
, и вызывается обработчик завершения, когда запись выполнена. Если вам нужно сериализовать эту операцию, это немного сложнее, и вы должны прочитать документацию по нитям здесь .