2016-12-17 4 views
1

Я пишу некоторые функции, которые используют clojure.async для абстракции от чтения/записи до сокетов. Мое намерение состоит в том, что значения могут быть помещены в канал, чтобы они были написаны, и выталкивались из канала для чтения. Таким образом, пользователю не нужно беспокоиться о Reader s/Writer s.BufferedReader - Блок в конце потока

Приведенный ниже код считывается из сокета в цикле, пересылая все, что он читает в канал. Канал возвращается, чтобы его можно было прочитать. Моя проблема в том, что когда конец потока достигнут, а не блокируется, он сидит там, пока что-то не читается. Если у меня будет несколько таких процессов, это вызовет заметную проблему с производительностью на моем компьютере.

Самый простой способ, которым я мог это исправить, - это как-то сделать BufferedReaderreadLine блок на EOF вместо возврата nil. Из того, что я вижу, это невозможно. Это меня не удивляет, поскольку блокировка EOF для большинства потоков будет очень странной. Однако для потоков сокетов EOF, похоже, не имеет определенного значения, поскольку сообщения могут быть получены даже после достижения EOF.

Есть ли способ предотвратить вращение цикла при ожидании ввода после достижения EOF?

(ns chat.so-example 
    (:require [clojure.core.async :as a :refer [chan go >!]]) 
    (:import [java.net Socket SocketException] 
      [java.io BufferedReader InputStreamReader InputStream])) 

(defn valid-message? [msg] 
    (and (some? msg) 
     (not (empty? msg)))) 

(defn new-input-chan [^Socket sock] 
    (let [^InputStream in (.getInputStream sock) 
     buff-reader  (BufferedReader. (InputStreamReader. in)) 
     in-chan   (chan)] 
    (go 
     (try 
     (while (.isConnected sock) ; This spins when EOF is reached 
      (let [line (.readLine buff-reader)] 
      (when (valid-message? line) 
       (>! in-chan line)))) 
     (catch SocketException se 
      (#_"Handle exception")) 
     (finally 
      (a/close! in-chan)))) 
    in-chan)) 
+0

Rich Hickey несколько раз заявлял, что это не очень хорошая идея, а не то, что он имел в виду для core.async. Возможно, вы захотите посмотреть его основные разговоры. Полностью понять обоснование этого. Посмотрите на библиотеки Zach Tellmans, такие как коллекторы и алефы, которые реализуют абстракцию, которую вы ищете, и интегрируетесь с core.async. –

+0

@LeonGrapenthin Есть ли у вас ссылки на разговоры? Я бы хотел их посмотреть. :-) – jszakmeister

ответ

0

Единственное, что крутит здесь, это вы. Ни один из методов в классах, которые вы используете, вращается. Прочтите документацию для isConnected и isClosed или isInputShutdown, и решение должно стать ясным. Это также помогло бы вам прочитать описание readLine, в котором очень четко сказано, что он возвращает, когда больше нет ввода для чтения.

+0

Я не совсем уверен, что этот ответ должен отвечать. Я * прочитал документы для 'getLine'. Следует отметить, что часть «возвращает ... null, если конец потока достигнут». Когда возвращается null с указанием конца потока, '(строка valid-message?)' Становится ложной, в результате чего цикл выходит из-под контроля, поскольку он больше не стоит на парковке ''! '. Я хочу знать, как предотвратить быстрый цикл. И я осознал свою ошибку в использовании 'inConnected', и попробовал только цикл, пока сокет открыт, а не подключен, и поведение оставалось прежним. – Carcigenicate

+0

Это также ссылка на ответ, которая, как вы знаете, бедна. – Carcigenicate

+1

Когда вы читаете конец потока, вы находитесь в конце потока. Никогда не будет другого входящего сообщения. Это то, что означает конец потока. Это не значит «ничего прямо сейчас». – amalloy

0

Метод java.io.BufferedReader.ready() может проверить, есть ли еще что-то прочитанное, даже после EOF. Кроме того, Sockets можно принудительно ввести в BufferedReader, используя (clojure.java.io/reader socket). То же самое для писателя.

Я не очень хорошо знаком с core.async, поэтому понятия не имею, если это поможет.