Страницы

Поиск по вопросам

понедельник, 8 апреля 2019 г.

Многопоточность и ассинхронность

пытаюсь понять что такое асинхронность. Ранее смотрел несколько статей\видео по этому поводу, но с каждым последующим просмотром - совсем запутался. В одном видео говорилось про то что
потоки - это подобие дополнительных рабочих, но их проблема в том что пока работает один поток другие ожидают его окончание.
асинхронность - это возможность давать команды на выполнение определенного когда,и в этот момент может паралельно выполнять свои задачи.
На этом объяснении я завис. Когда я писал сервер\ клиент. В сервере организовывал несколько потоков которые выполнялись паралельно. У меня был метод в левом потоке на количество клиентов подключенных к серверу, поток для подключения новых клиентов, и главный поток.
Возможно я чего-то не доганяю. Может это все происходит слишком быстро и это не замечается.
В общем, объясните пожалуйста


Ответ

Прежде всего надо разобраться с терминологией. В самом простом виде программа выполняет последовательно или синхронно, инструкция за инструкцией, в том порядке, в котором они идут в коде.
System.out.println("A"); System.out.println("B"); System.out.println("C");
Каждый вызов println блокирующийся. Это значит, что пока он не закончится, программа не будет выполняться дальше.
В реальном мире от программы требуется реагировать на множество событий, происходящих в произвольные моменты времени, или одновременно выполнить задачи существенно более долгие, чем вывод на экран. Такое выполнение называется многозадачным. Но в этом месте часто возникает путаница, так как вместо этого часто, особенно в мире Java, используют термин "асинхронное выполнение", а у этого термина есть своё значение, более конкретное.
Чтобы лучше разобраться в истинном значении терминов, приведу бытовую аналогию:
Один человек ставит кастрюлю на плиту, ждёт пока она закипит, закидывает пельмени, ждёт пока сварятся, снимает, ставит вторую, ждёт пока закипит... - последовательное однопоточное выполнение. Один человек ставит две кастрюли на плиту, как только одна из них закипела, забрасывает пельмени и т.д. - асинхронное выполнение. Два человека ставят две кастрюли... - многопоточное выполнение.
В данной аналогии плита - это аппаратный ресурс (процессор), пельмени - программный ресурс (сокеты), человек - это поток, а кипение - блокирующийся вызов.
Примечание: К аналогии можно добавить ещё вариант "Два человека ставят две кастрюли на две плиты", описывающий действительную параллельность в многопроцессорной системе. Но с описанием видов многозадачности можно легко выйти за ограничение по количеству символов на ответ.
Очевидно, что при асинхронном выполнении ресурсов требуется меньше, а скорость та же, что и при многопоточном. Именно поэтому проблему c10k могут решать только асинхронные сервера. Минусы в том, что асинхронный код зачастую сложнее и не всё можно выполнять асинхронно.
В вашем прошлом вопросе я уже показывал примеры многопоточного и асинхронного кода:
Многопоточный код
ExecutorService executor = Executors.newFixedThreadPool(10); while (true) { // Вызов accept() блокирующийся и остановит цикл до тех пор // пока не поступит новое соединение SocketChannel socketChannel = serverSocketChannel.accept(); // При этом будет запущен отдельный поток, который будет // обслуживать только это соединение executor.execute(() -> { // Отправка файл, которая займёт 5 секунд например }); // Выполнение продолжится сразу после вызова execute, // меньше чем через миллисекунду. Если подключится ещё один // клиент, для него будет запущен ещё один поток, и будут // передаваться два файла одновременно двум разным клиентам. }
Асинхронный код
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); while (true) { // Цикл событий selector.select(); // Мультиплексирование Set keys = selector.selectedKeys(); Iterator iterator = keys.iterator(); while (iterator.hasNext()) { SelectionKey key = iterator.next();
if (key.isAcceptable()) { // Поступило новое соединение SocketChannel socketChannel = serverSocketChannel.accept(); socketChannel.configureBlocking(false); // Регистрируем его для обработки в этом же потоке // на следующих итерациях цикла socketChannel.register(selector, SelectionKey.OP_READ, new Storage()); }
if (key.isReadable()) { // В одном из соединений появились данные SocketChannel socketChannel = (SocketChannel) key.channel(); ByteBuffer buffer = ByteBuffer.allocate(256);
// Читаем столько данных, сколько сейчас есть int read = socketChannel.read(buffer); if (read != -1) { ... } else { // Ничего не прочиталось, значит пора закрывать соединение socketChannel.close(); } }
iterator.remove(); } }
В примере асинхронного кода нет ни одного блокирующегося вызова, цикл никогда не останавливается. Какой вариант многозадачности выбрать - это вопрос имеющий смысл только в контексте конкретной задачи. У каждого варианта свои плюсы и минусы. Из примеров видно, что асинхронного кода требуется больше, но при асинхронном выполнении значительно реже возникает ситуация, когда два человека дерутся за одну пустую кастрюлю, пока в другой догорают пельмени

Комментариев нет:

Отправить комментарий