web-dev-qa-db-fra.com

Comment interrompre readLine de BufferedReader

J'essaye de lire l'entrée d'un socket ligne par ligne dans plusieurs threads. Comment puis-je interrompre readLine() pour pouvoir arrêter en douceur le thread bloqué?

EDIT (bounty) : Cela peut-il être fait sans fermer la prise?

35
Jack Edmonds

Fermez le socket sur le thread d'interruption. Cela provoquera une exception sur le thread interrompu.

Pour plus d'informations sur ce problème et sur d'autres problèmes de concurrence, je recommande vivement le livre de Brian Goetz "Java Concurrency in Practice".

20
Steve Emmerson

Sans fermer la prise:

Le problème difficile n'est pas le BufferedReader.readLine, mais la read sous-jacente. Si un thread est bloqué en lecture, le seul moyen de le démarrer est de fournir des données réelles ou de fermer le socket (interrompre le thread devrait probablement fonctionner, mais en pratique ne fonctionne pas).

Donc, la solution évidente est d'avoir deux threads. Celui qui lit les données brutes et restera bloqué. Le second sera le fil appelant readLine. Tuyau données de la première à la seconde. Vous avez alors accès à un verrou qui peut être utilisé pour réveiller le deuxième thread et lui demander de prendre les mesures appropriées.

Il y a des variations. Vous pourriez avoir le premier thread utilisant NIO, avec une seule instance de thread partagée entre tous les consommateurs.

Sinon, vous pouvez écrire une readLine qui fonctionne avec NIO. Cela pourrait même prendre une forme relativement simple à un seul thread, puisque Selector.wakeup existe et fonctionne.

25

Je jouais avec cela récemment (avec Scala) et je n’ai pas aimé la réponse acceptée selon laquelle il faut fermer la prise et obtenir une exception.

Finalement, j'ai découvert qu'il était possible d'appeler socket.shutdownInput() dans le thread d'interruption pour sortir de l'appel readLine sans exception. Je passe cet appel dans un gestionnaire SIGINT afin de pouvoir nettoyer et fermer le socket dans le thread principal.

Notez que l’équivalent existe pour le flux de sortie avec socket.shutdownOutput()

7
bnsmith

Извините за опоздание более чем на 6 лет ;-) Мне понадобилась какая-то прерываемая readLine при чтении с клавиатуры, для простого консольного приложения для хобби. Другими словами, я не мог "закрыть розетку".

Как вы, возможно, знаете, System.in - то InputStream, который, очевидно, уже выполняет некотореEnter]). Тем не менее, кажется, что для большей эффективности

BufferedReader consoleIn = new BufferedReader(new InputStreamReader(System.in));

Другая вещь, которую можно было бы обнаружить, заключается в том, что BufferedReader.readLine() блокируется до тех пор, пока не будет введен ввод (даже если поток прерывается, что, по-видимому, завершает поток только после того, как readline() получает свой ввод). Однако можно предсказать, когда BufferedReader.read() будет не блокируется, вызвав BufferedReader.ready() == true. (Nom: == false не гарантирует блок, il y a lieu.)

Таким образом, я включил вышеупомянутые идеи в метод, который читает символ BufferedReader символьно, проверяя между каждым символом, был ли поток прерван, а также проверяет конец строки, после чего строка текста возвращается.

Cliquez ici pour en savoir plus sur BufferedReader, sur notre site. _consoleIn_. (Критика тоже может приветствоваться ...)

private String interruptibleReadLine(BufferedReader reader)
        throws InterruptedException, IOException {
    Pattern line = Pattern.compile("^(.*)\\R");
    Matcher matcher;
    boolean interrupted = false;

    StringBuilder result = new StringBuilder();
    int chr = -1;
    do {
        if (reader.ready()) chr = reader.read();
        if (chr > -1) result.append((char) chr);
        matcher = line.matcher(result.toString());
        interrupted = Thread.interrupted(); // resets flag, call only once
    } while (!interrupted && !matcher.matches());
    if (interrupted) throw new InterruptedException();
    return (matcher.matches() ? matcher.group(1) : "");
}

... et toutes les informations sur le comportement, les règles de jeu, les règles.

Vous trouverez des informations sur Java 8 dans Linux.

5
fr13d

vous pouvez concevoir une classe Timer autour du bloc read ().

vous devez définir un délai d'attente pour votre minuterie.

sur timeout, interrompez simplement votre thread.

0
mhshams

Sans fermer le socket, il ne fait aucun doute que la meilleure solution, avec le moins de temps système possible, consiste simplement à éviter d'utiliser les méthodes de blocage read jusqu'à ce que BufferedReader soit prêt ou qu'un délai d'attente soit atteint.

public String readLineTimeout(BufferedReader reader, long timeout) throws TimeoutException, IOException {
    long start = System.currentTimeMillis();

    while (!reader.ready()) {
        if (System.currentTimeMillis() - start >= timeout)
            throw new TimeoutException();

        // optional delay between polling
        try { Thread.sleep(50); } catch (Exception ignore) {}
    }

    return reader.readLine(); // won't block since reader is ready
}
0
Cord Rehn

Si vous souhaitez utiliser readLine sur un socket de serveur dans une architecture TCP client-serveur, par exemple, vous pouvez utiliser setSoTimeout(int timeout) of Java.net.Socket.

Depuis le Socket # setSoTimeout (int timeout) Documentation :

Activer/désactiver SO_TIMEOUT avec le délai d'expiration spécifié, en millisecondes. Lorsque cette option est définie sur un délai d'expiration autre que zéro, l'appel a read () sur le flux d'entrée associé à ce socket bloquera uniquement pendant cette durée. Si le délai expire, une Java.net.SocketTimeoutException est déclenchée, même si le Socket est toujours valide.

public class MainApp {
    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        ServerSocket serverSocket = new ServerSocket(11370);
        Socket clientSocket = serverSocket.accept();
        clientSocket.setSoTimeout(2000);
        executorService.execute(new ReadingThread(clientSocket));
        // ... some async operations
        executorService.shutdown();
    }
}

public class ReadingThread implements Runnable {
    private final Socket clientSocket;
    public ReadingThread(Socket clientSocket) {
        this.clientSocket = clientSocket;
    }

    @Override
    public void run() {
        BufferedReader socketReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
        String readInput = null;
        while (!Thread.currentThread().isInterrupted()) {
            try {
                readInput = socketReader.readLine();
            } catch (SocketTimeoutException e) {
                continue; 
            }
        }
        // operations with readInput
    }
}

L'application principale implémente un socket de serveur qui écoute les connexions et possède un pool de threads. Si une communication client entrante est acceptée, un nouveau thread du pool est affecté et la fonction d'exécution est appelée dans ReadingThread (peut être ajustée pour autoriser plusieurs threads). Sur la socket utilisée pour communiquer au client, la propriété setSoTimeout(int timeout) a été définie. Par conséquent, si readLine ne retourne pas dans le délai spécifié, une exception SocketTimeoutException est levée. Vous pouvez vérifier dans une boucle si la ReadingThread a été interrompue par l’application principale et, le cas échéant, arrêter de lire à partir de la socket.

0
Bernhard_S