Question

Si j'écrivais:

int selectedChannels = selector.select();
Set selectedKeys = selector.selectedKeys();
if ( selectedChannels != selectedKeys.size() ) {
    // Selector.select() returned because of a call to Selector.wakeup()
    // so do synchronization.
}
// Continue with handling selected channels.

détecterait-il correctement l'appel de réveil?

Informations de base:

J'écris un serveur qui, la plupart du temps, ne reçoit que des paquets et les stocke dans un fichier. Très rarement, l'application a la nécessité de s'envoyer un paquet spécial. Pour cela, il établit une connexion (à partir d’un autre thread) au socket du serveur:

SocketChannel channel = SocketChannel.open();
channel.configureBlocking( false );
channel.connect( new InetSocketAddress( InetAddress.getLocalHost(), PORT ));
selector.wakeup();
SelectionKey key = channel.register( selector, SelectionKey.OP_CONNECT );

Le problème est que SelectableChannel.register () peut se bloquer si le thread principal est déjà dans Selector.select (). Pour éviter cela, j'appelle Selector.wakeup (), qui permet au thread principal de revenir prématurément à select (). Pour m'assurer que l'autre thread a la possibilité de terminer l'appel register, je devrais synchroniser le fil principal, mais je le ferais après tous les retours de select (). Si je pouvais détecter si elle est retournée par select () à cause d'un appel à wakeup (), je pourrais alors l'optimiser pour ce cas uniquement.

Donc, en théorie, l'extrait de code supérieur devrait fonctionner, mais je me demandais s'il ne le ferait que parce qu'il repose sur un comportement non spécifié?

Merci pour vos remarques.

Était-ce utile?

La solution

Je suppose que l'extrait de code proposé ne fonctionnerait pas du tout en principe, conformément aux contrats de Selector # select () et de Selector # selectedKeys () . De Sélecteur :

  
      
  • Le jeu de clés sélectionné est le jeu de clés tel que le canal de chaque clé a été détecté comme étant prêt pour au moins une des opérations identifiées dans le jeu d'intérêt de la clé lors d'une opération de sélection antérieure. Cet ensemble est renvoyé par la méthode selectedKeys.
  •   
public abstract int select(long timeout)
                throws IOException
    Returns:
        The number of keys, possibly zero, whose ready-operation sets were
        updated

Comme je l'ai lu, la taille de l'ensemble selectedKeys doit toujours être égale au nombre renvoyé par select par définition. Comme vous le savez peut-être aussi, j’ai remarqué que certaines implémentations ne suivent pas tout à fait la documentation. En fait, selectedKeys renvoie toutes les clés contenant des ensembles prêts à être mis à jour, même s'ils n'ont pas été mis à jour un appel à sélectionnez . Le seul autre indicateur que la sélection a réveillé en raison d'un appel à réveil pourrait être que le nombre de clés est égal à zéro; cependant, l'une ou l'autre méthode ne serait pas fiable, au mieux.

La manière habituelle de gérer cela est, comme implicite, par le contrôle de la simultanéité. Je ne m'inquiéterais pas du temps d'exécution ici; il s'agit d'un exemple classique de optimisation prématurée .

Sauf si vous vous inquiétez vraiment des tolérances à la microseconde à un chiffre, vous ne remarquerez aucun ralentissement. Si vous êtes inquiet à propos de ce niveau de tolérance, un Selector ne sera pas fiable. assez pour vous de toute façon.

Voici un exemple du mécanisme habituel, utilisant un ReentrantLock pour réaliser la concurrence simultanée appropriée:

ReentrantLock selectorGuard;
Selector selector;

private void doSelect() {
    // Don't enter a select if another thread is in a critical block
    selectorGuard.lock();
    selectorGuard.unlock();

    selector.select();
    Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();

    while(keyIter.hasNext()) {

        SelectionKey key = keyIter.next();
        keyIter.remove();

        // Process key
    }
}

private void addToSelector() {

    // Lock the selector guard to prevent another select until complete
    selectorGuard.lock();

    try {
        selector.wakeup();

        // Do logic that registers channel with selector appropriately

    } finally {
        selectorGuard.unlock();
    }
}

Autres conseils

Je ne comprends pas pourquoi votre code fonctionnerait en général.

Pourquoi ne pas simplement vérifier un volatile après select ?

Si select () renvoie zéro, le délai a expiré ou il a été réveillé.

Vous ne pouvez pas vraiment être sûr que la seule raison pour laquelle le sélecteur s'est réveillé était due au réveil. Vous pouvez également avoir une activité de socket.

Ainsi, vous devez faire en sorte que l'appelant de réveil fasse quelque chose comme définir un booléen volatil pour indiquer son désir d'attirer l'attention. La boucle de sélection peut vérifier la valeur de ce booléen à chaque réveil.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top