Как сделать так, чтобы поток присоединился к другому потоку, а ожидал только n секунд процессорного времени? - PullRequest
0 голосов
/ 26 октября 2010

otherThread.join( time ), кажется, ожидает time в реальных миллисекундах. Я хочу подождать time в реальном времени процессора, чтобы я мог получить согласованное поведение в своем приложении.

Я быстро взглянул на ThreadMXBean, но, похоже, это не совсем то, что я хотел (он сообщает мне фактическое время процессора, но не предлагает удобного способа подождать, пока пройдет некоторое время). Занятый цикл вокруг sleep() может сработать, но кажется крайне неэффективным.

Я также думал об использовании другого потока и ожидании на Condition, но я не уверен, как это будет работать. Основной поток будет делать: myCondition.await(), где другой поток будет переключать myCondition, когда otherThread использовал time фактическое время процессора. Опять же, это кажется сложным и, вероятно, по-прежнему требует, чтобы управляющий поток имел занятый цикл.

Редактировать: Я делаю это для сценария оценки. Это означает, что мне нужен способ тайм-аута, если ученик находится в бесконечном цикле, и это должно быть справедливым. Я использовал JUnit для запуска тестов на учениках, но у него та же проблема с тайм-аутом: если одна и та же (неэффективная) отправка выполняется несколько раз, она может получить разные оценки в зависимости от того, какие другие задания выполняются на машине в то время (реальная проблема для групповой работы).

Но это проблема и с обычным модульным тестированием, тоже - используя тактовое время вместо процессорного времени, JUnit получает противоречивые результаты теста?

Ответы [ 4 ]

2 голосов
/ 26 октября 2010

Я бы рекомендовал запускать один тест JVM за раз и использовать time (см. man time для определения фактического используемого времени ЦП). Что касается прерывания, если это занимает слишком много времени ... Вы могли бы сделать это с помощью сценария оболочки или просто позволить процессу запускаться до завершения и выставлять оценки по фактическому времени.

1 голос
/ 28 октября 2010

Я думаю, что если вы объедините стратегию thread.join () / interrupt () (для отсрочки неудачного кода (бесконечные циклы)) и testThread, вызывая процессор, вы получите то, что хотите, если я что-то не пропустил.Решение может следовать подходу, подобному:

public class GradingThread extends Thread {
    private Thread testThread;
    private long elapsedClockTime=-1;
    public GradingThread(TestingThread testThread){
        this.testThread = testThread;
        testThread.setGradingThread(this);
    }
    public void setElapsed(long elapsedClockTime){
        this.elapsedClockTime = elapsedClockTime;
    }
    public void run(){
        System.out.println("GradingThread ID="+Thread.currentThread().getId());
        try{
        testThread.start();
        testThread.join(5000);
        testThread.interrupt();
        }catch(Exception e){e.printStackTrace();}
        if(elapsedClockTime==-1){
            System.out.println("Student program timedout (more than 5000 clock seconds)");
        }else{
            System.out.println("Student program elapsed cpu time = "+(elapsedClockTime)/1000000+"ms");
        }
    }
    public static void main(String[] args) {
        (new GradingThread(new TestingThread())).start();
    }
}
public class TestingThread extends Thread {
    private GradingThread gradingThread;
    public void setGradingThread(GradingThread thread){
        gradingThread = thread;
    }
    public void run(){
        StudentProgram sp = new StudentProgram();
        System.out.println("TestingThrad ID="+Thread.currentThread().getId());
        long scpu = getCpuTime();
            sp.takeLessThan5WallSecondsToRun(); //try calling infiniteLoop() too.
        long ecpu = getCpuTime();
        gradingThread.setElapsed(ecpu - scpu);
    }
    /** Get CPU time in nanoseconds. */
    public long getCpuTime( ) {
        ThreadMXBean bean = ManagementFactory.getThreadMXBean( );
        if ( ! bean.isCurrentThreadCpuTimeSupported())
            return -1L;
        long time = bean.getThreadCpuTime(Thread.currentThread().getId());       
        return time;
    }
}//end of TestingThread.
class StudentProgram {
    public void infiniteLoop(){
        while(true);
    }
    public int takeLessThan5WallSecondsToRun(){
        int total=0;
        while(total < Integer.MAX_VALUE) total++;
        return total;   
    }
}//end of StudentProgram.
1 голос
/ 26 октября 2010

Подумайте о том, что вы просите: вы хотите, чтобы поток блокировал (не использовал процессорное время), пока он не использовал указанное количество процессорного времени. Это не имеет смысла.

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

1 голос
/ 26 октября 2010

Не произойдет. Timed join() основано на времени настенных часов, что означает, что аппаратный таймер может быть настроен на предоставление асинхронного прерывания x секунд, если поток еще не завершен. Поскольку нет способа узнать a priori , сколько ЦП будет использовать поток, и, следовательно, нет способа запланировать прерывание при достижении некоторой границы. Время ЦП учитывается, когда процесс добровольно или принудительно отдает свой временной интервал, поэтому в любом случае не будет никакого способа достичь какой-то точной цифры.

Самое близкое, что вы сможете получить - это периодически проверять загрузку ЦП и вызывать interrupt() для потоков, которые превысили лимит. (Внимательно изучите семантику этого, потому что прерывание потока не обязательно приведет к немедленной остановке потока.) Если все, что вас волнует, это то, потреблял ли поток больше x секунд процессорного времени поздние проверки, которые получают результаты, такие как kx , где k> 1 , не имеют значения. Они все еще больше, чем x , и этого достаточно, чтобы знать, что ваш кандидат превысил предел.

Одна вещь, которую вы могли бы сделать, если вы работаете в системе Unix-y, - это запустить все назначение как процесс и использовать ulimit, чтобы ограничить количество ЦП, которое ему разрешено, до некоторого значения с разумным количеством для Запуск JVM и загрузка программы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...