Потоки последствия AsynchronousByteChannel

Javadoc для AsynchronousByteChannel.read() говорит, что операция выполняется асинхронно, но что произойдет, когда будет достигнут конец потока? Разрешено ли реализации запускать обработчик завершения в том же потоке, который вызвал read()? С точки зрения реализации нет смысла выполнять эту операцию асинхронно, потому что мы уже знаем результат. Точно так же, если пользователь пытается прочитать в байтовом буфере, где оставшаяся функция() возвращает 0, мы знаем, что операция чтения должна вернуть 0.

Я спрашиваю, потому что я столкнулся с состоянием гонки в моей собственной реализации AsynchronousByteChannel. Я вызываю обработчик завершения, который сам вызывает notify() после завершения операции. Затем я вызываю следующий пользовательский код:

CompletionHandler<?, ?> handler = ...;
synchronized (handler)
{
  asyncByteChannel.read(handler);
  handler.wait();
}

Обратите внимание, что пользователь предполагает, что обработчик будет уведомлен, когда операция завершится, но поскольку read() фактически вызывает обработчик завершения синхронно, он получает уведомление до ожидания(), и последний будет заблокирован навсегда.

Требует ли спецификация обновления CompletionHandler в отдельном потоке или пользователи должны знать, что поток, который вызывает read(), может выполнять некоторые операции синхронно?


person Gili    schedule 12.07.2011    source источник
comment
Его многопоточная модель — беспорядок. Javadoc в асинхронной группе невозможно разобрать. Потоки должны быть оставлены разработчикам — это легкая часть. К сожалению, они решили помочь нам в этом. Результат очень сложный, неинтуитивный, плохо документированный (потому что они не могут объяснить беспорядок). Случайный обозреватель может найти API в порядке - действительно, почему это должно быть сложно? Но когда кто-то серьезно строит приложение поверх него, сложности сведут его с ума.   -  person irreputable    schedule 13.07.2011


Ответы (2)


Даже когда обработчик вызывается в другом потоке, нет гарантии, что он будет вызван после возврата метода read, то есть после запуска вашего wait(). (Хорошо, блокировка синхронизации, кажется, гарантирует это.)

Вы должны использовать синхронизацию и логическую переменную для ожидания и блокировки:

CompletionHandler<?, ?> handler = ...;
synchronized (handler)
{
   asyncByteChannel.read(handler);
   while(!handler.finished) {
     handler.wait();
   }
}

... и ваш обработчик установит для переменной finished значение true.

person Paŭlo Ebermann    schedule 12.07.2011
comment
Предлагаемое вами решение прекрасно, если мне разрешено обрабатывать CompletionHandler в основном потоке. Так ли это, пока неясно :) - person Gili; 12.07.2011
comment
Нет, решение можно использовать всегда. (Это не обязательно, если это не разрешено, но вы все равно должны ждать в цикле, так как могут быть ложные пробуждения.) - person Paŭlo Ebermann; 12.07.2011
comment
Я согласен, но вопрос не об этом. Речь идет о том, позволяет ли спецификация уведомлять в том же потоке. - person Gili; 12.07.2011

Глядя на http://www.docjar.com/html/api/sun/nio/ch/AsynchronousSocketChannelImpl.java.html они всегда обновляют CompletionHandler в отдельном потоке, но Future обновляется в том же потоке. Найдите переменную hasSpaceToRead, чтобы найти рассматриваемый метод.

Я предполагаю, что их цепочка рассуждений выглядит примерно так:

  1. Мы создаем Future, возвращаемый пользователю, поэтому он никак не может с ним взаимодействовать (синхронизировать и т. д.) до того, как мы вернем объект.
  2. Пользователь создает CompletionHandler, поэтому у нас нет возможности контролировать то, что делает реализация (насколько нам известно, мы можем вызвать взаимоблокировку!). Не рискуйте, запускайте обработчик завершения в отдельном потоке.

ОБНОВЛЕНИЕ: я исправлен. Согласно Invoker.invoke() "Если текущий поток находится в пуле потоков группы каналов, тогда обработчик вызывается напрямую, в противном случае он вызывается косвенно».

РЕШЕНО: согласно http://download.oracle.com/javase/7/docs/api/java/nio/channels/AsynchronousChannelGroup.html «Где операция ввода-вывода завершается немедленно, а инициирующий поток является одним из потоков в группе, то обработчик завершения может быть вызван непосредственно инициирующим потоком».

person Gili    schedule 12.07.2011