Question

Que dois-je utiliser pour obtenir la sémantique équivalente à AutoResetEvent en Java? (Voir cette question ManualResetEvent).

Était-ce utile?

La solution

@ réponse de user249654 semblait prometteur. J'ai ajouté quelques tests unitaires pour le vérifier, et en fait il fonctionne comme prévu.

J'ai aussi ajouté une surcharge de waitOne qui prend un temps mort.

Le code est ici au cas où quelqu'un d'autre trouve utile:

Test Unit

import org.junit.Assert;
import org.junit.Test;

import static java.lang.System.currentTimeMillis;

/**
 * @author Drew Noakes http://drewnoakes.com
 */
public class AutoResetEventTest
{
    @Test
    public void synchronisesProperly() throws InterruptedException
    {
        final AutoResetEvent event1 = new AutoResetEvent(false);
        final AutoResetEvent event2 = new AutoResetEvent(false);
        final int loopCount = 10;
        final int sleepMillis = 50;

        Thread thread1 = new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                try {
                    for (int i = 0; i < loopCount; i++)
                    {
                        long t = currentTimeMillis();
                        event1.waitOne();
                        Assert.assertTrue("Time to wait should be within 5ms of sleep time",
                                Math.abs(currentTimeMillis() - t - sleepMillis) < 5);
                        Thread.sleep(sleepMillis);
                        t = currentTimeMillis();
                        event2.set();
                        Assert.assertTrue("Time to set should be within 1ms", currentTimeMillis() - t <= 1);
                    }
                } catch (InterruptedException e) {
                    Assert.fail();
                }
            }
        });

        Thread thread2 = new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                try {
                    for (int i = 0; i < loopCount; i++)
                    {
                        Thread.sleep(sleepMillis);
                        long t = currentTimeMillis();
                        event1.set();
                        Assert.assertTrue("Time to set should be within 1ms", currentTimeMillis() - t <= 1);
                        t = currentTimeMillis();
                        event2.waitOne();
                        Assert.assertTrue("Time to wait should be within 5ms of sleep time",
                                Math.abs(currentTimeMillis() - t - sleepMillis) < 5);
                    }
                } catch (InterruptedException e) {
                    Assert.fail();
                }
            }
        });

        long t = currentTimeMillis();

        thread1.start();
        thread2.start();

        int maxTimeMillis = loopCount * sleepMillis * 2 * 2;

        thread1.join(maxTimeMillis);
        thread2.join(maxTimeMillis);

        Assert.assertTrue("Thread should not be blocked.", currentTimeMillis() - t < maxTimeMillis);
    }

    @Test
    public void timeout() throws InterruptedException
    {
        AutoResetEvent event = new AutoResetEvent(false);

        int timeoutMillis = 100;
        long t = currentTimeMillis();
        event.waitOne(timeoutMillis);
        long took = currentTimeMillis() - t;
        Assert.assertTrue("Timeout should have occurred, taking within 5ms of the timeout period, but took " + took,
                Math.abs(took - timeoutMillis) < 5);
    }

    @Test
    public void noBlockIfInitiallyOpen() throws InterruptedException
    {
        AutoResetEvent event = new AutoResetEvent(true);

        long t = currentTimeMillis();
        event.waitOne(200);
        Assert.assertTrue("Should not have taken very long to wait when already open",
                Math.abs(currentTimeMillis() - t) < 5);
    }
}

AutoResetEvent avec surcharge qui accepte un délai d'attente

public class AutoResetEvent
{
    private final Object _monitor = new Object();
    private volatile boolean _isOpen = false;

    public AutoResetEvent(boolean open)
    {
        _isOpen = open;
    }

    public void waitOne() throws InterruptedException
    {
        synchronized (_monitor) {
            while (!_isOpen) {
                _monitor.wait();
            }
            _isOpen = false;
        }
    }

    public void waitOne(long timeout) throws InterruptedException
    {
        synchronized (_monitor) {
            long t = System.currentTimeMillis();
            while (!_isOpen) {
                _monitor.wait(timeout);
                // Check for timeout
                if (System.currentTimeMillis() - t >= timeout)
                    break;
            }
            _isOpen = false;
        }
    }

    public void set()
    {
        synchronized (_monitor) {
            _isOpen = true;
            _monitor.notify();
        }
    }

    public void reset()
    {
        _isOpen = false;
    }
}

Autres conseils

class AutoResetEvent {

  private final Object monitor = new Object();
  private volatile boolean open = false;

  public AutoResetEvent(boolean open) {
    this.open = open;
  }

  public void waitOne() throws InterruptedException {
    synchronized (monitor) {
      while (open == false) { 
        monitor.wait();
      }
      open = false; // close for other
    }

  }

  public void set() {
    synchronized (monitor) {
      open = true;
      monitor.notify(); // open one 
    }
  }

  public void reset() {//close stop
    open = false;
  }
}

Je suis en mesure d'obtenir CyclicBarrier de travailler pour mes besoins.

Voici le code C # Je tentais de reproduire en Java (il est juste un programme de démonstration, j'ai écrit pour isoler le paradigme, je l'utilise maintenant dans les programmes C # Je vous écris pour générer la vidéo en temps réel, afin de fournir un contrôle précis de la frame rate):

using System;
using System.Timers;
using System.Threading;

namespace TimerTest
{
    class Program
    {
        static AutoResetEvent are = new AutoResetEvent(false);
        static void Main(string[] args)
        {
            System.Timers.Timer t = new System.Timers.Timer(1000);
            t.Elapsed += new ElapsedEventHandler(delegate { are.Set(); });
            t.Enabled = true;
            while (true)
            {
                are.WaitOne();
                Console.WriteLine("main");
            }
        }
    }
}

et est ici le code Java je suis venu avec pour faire la même chose (en utilisant la classe CyclicBarrier comme suggéré dans une réponse précédente):

import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.CyclicBarrier;

public class TimerTest2 {
    static CyclicBarrier cb;

    static class MyTimerTask extends TimerTask {
        private CyclicBarrier cb;
        public MyTimerTask(CyclicBarrier c) { cb = c; }

        public void run() { 
            try { cb.await(); } 
            catch (Exception e) { } 
        }
    }

    public static void main(String[] args) {
        cb = new CyclicBarrier(2);
        Timer t = new Timer();
        t.schedule(new MyTimerTask(cb), 1000, 1000);

        while (true) {
            try { cb.await(); } 
            catch (Exception e) { }
            System.out.println("main");
        }
    }
}

Une autre extension à la solution de la réponse acceptée au cas où vous voulez savoir si votre attente terminé avec délai d'attente ou ensemble d'événements (ce qui est exactement ce que .NET AutoResetEvent fait).

public boolean waitOne(long timeout) throws InterruptedException {
    synchronized (monitor) {
        try {
            long t = System.currentTimeMillis();
            while (!isOpen) {
                monitor.wait(timeout);
                // Check for timeout
                if (System.currentTimeMillis() - t >= timeout)
                    break;
            }

            return isOpen;
        }
        finally {
            isOpen = false;
        }
    }
}
import java.util.concurrent.TimeUnit;

import java.util.concurrent.locks.Condition;

import java.util.concurrent.locks.ReentrantLock;

public class AutoResetEvent {

  private volatile boolean _signaled;
  private ReentrantLock _lock;
  private Condition _condition;

  public AutoResetEvent(boolean initialState) {
    _signaled = initialState;
    _lock = new ReentrantLock();
    _condition = _lock.newCondition();
  }

  public void waitOne(long miliSecond) throws InterruptedException {
    _lock.lock();
    try {
      while (!_signaled)
        _condition.await(miliSecond, TimeUnit.MILLISECONDS);
      _signaled = false;
    } finally {
        _lock.unlock();
    }
  }

  public void waitOne() throws InterruptedException {
    _lock.lock();
    try {
      while (!_signaled)
        _condition.await();
      _signaled = false;
    } finally {
        _lock.unlock();
    }
  }

  public void set() {
    _lock.lock();
    try {
      _condition.signal();
      _signaled = true;
    } finally {
      _lock.unlock();
    }
  }

  public void reset() {
    _lock.lock();
    try {
      _signaled = false;
    } finally {
      _lock.unlock();
    }
  }
}

Je crois que ce que vous cherchez est soit un CyclicBarrier ou CountDownLatch.

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