Я провел обширные тесты и не смог сломать 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()
Обратите внимание, что запуск этого кода может заставьте ваш компьютер трэш.
Вы можете проверить это следующим образом:
Main.java:
package test;
import test.sub.B;
public class Main {
public static void main(String args[]) {
System.out.println("Creating object");
final B b = new B();
System.out.println("instanceof check");
if (b instanceof B) {
// Do nothing
}
}
}
A.java:
package test;
public class A {
static {
System.out.println("Class A loaded");
}
}
B.java:
[ 112]Вывод:
Creating object
Class B loaded
instanceof check
Итак, ни импорт, ни объявление (A a;
) не загружают класс.
Это соответствует утверждению из википедии: The Java Class Loader [...] dynamically loads Java classes [...]. [...] This loading is typically done "on demand", in that it does not occur until the class is called by the program. [...]
Когда вы изменяете A a;
в классе B
на A a = new A();
, тогда класс A
будет загружен.
Следует отметить две вещи:
Оператор import
не влияет на поведение класса во время выполнения. Никакой код не генерируется для оператора импорта. И если вы просто импортируете класс A в класс B, то загрузка класса B не приводит к загрузке класса A.
Время загрузки класса и время инициализации класса различаются.
Жизненный цикл класса заключается в том, что он загружается, связывается и инициализируется перед использованием (см. Глава 12 JLS ).
Загрузка выполняется загрузчиком классов и включает в себя поиск файла класса, чтение его в byte[]
и вызов ClassLoader::defineClass
.
Связывание выполняется с помощью основного кода JVM и состоит из проверки, подготовки и разрешения символических ссылок. В JLS 12.3 говорится следующее:
Эта спецификация обеспечивает гибкость реализации в отношении того, когда происходит связывание действий (и, из-за рекурсии, загрузки), при условии, что семантика Язык программирования Java соблюдается, что класс или интерфейс полностью проверены и подготовлены перед его инициализацией, и что ошибки, обнаруженные во время связывания, генерируются в той точке программы, где программа предпринимает некоторые действия, которые могут потребовать связи с классом. или интерфейс, связанный с ошибкой.
blockquote>Это означает, что мы не можем определиться с тем, когда выполняются различные задачи.
Инициализация происходит после того, как все соответствующие классы были связаны. В соответствии с JLS 12.4.1 :
Класс T или интерфейсный тип T будут инициализированы непосредственно перед первым появлением любого из следующего:
[ 1145] T является классом, и создается экземпляр T.
blockquote>- Вызывается статический метод, объявленный T.
- Статическое поле, объявленное T, присваивается.
- Используется статическое поле, объявленное T, и поле не является постоянной переменной (§4.12.4).
Согласно JLS (см. Выше), мы не можем точно сказать, когда B
будет загружен и связан. Все, что мы можем сказать, это то, что это происходит до инициализации B
... если она инициализирована. (Вы можете получить представление о порядке загрузки классов, включив некоторые журналы JVM. Однако, порядок может варьироваться в зависимости от поставщика и версии JVM.)
В соответствии с инициализацией JLS (см. Выше) [115 ] будет происходить (обычно), когда первый экземпляр B
создается вашим методом main
. Предполагая, что object
в вашем Вопросе было инициализировано для экземпляра B
, инициализация уже произошла до теста instanceof
. Если нет, то B
не будет инициализирован тестом instanceof
.
На первый взгляд, нет необходимости загружать класс A
. (Это зависит от того, действительно ли и как класс B
использует класс A
, и от того, как / как другие части вашей кодовой базы используют A
.)