Вычисление с ограничением по времени - PullRequest
8 голосов
/ 04 октября 2011

Я пытаюсь написать конструкцию, которая позволяет мне выполнять вычисления в заданном временном окне.Что-то вроде:

def expensiveComputation(): Double = //... some intensive math

val result: Option[Double] = timeLimited( 45 ) { expensiveComputation() }

Здесь timeLimited будет работать expensiveComputation с таймаутом 45 минут.Если он достигает тайм-аута, он возвращает None, иначе он обернул результат в Some.

Я ищу решение, которое:

  • довольно дешево по производительности ипамять;
  • Запустит лимитированное по времени задание в текущем потоке .

Есть предложения?

РЕДАКТИРОВАТЬ

Я понимаю, что моя первоначальная проблема не имеет решения.Скажем, я могу создать поток для расчета (но я предпочитаю не использовать пул потоков / executor / dispatcher).Какой самый быстрый, безопасный и чистый способ сделать это?

Ответы [ 9 ]

8 голосов
/ 04 октября 2011

Запускает указанный блок кода или выдает исключение по таймауту:

@throws(classOf[java.util.concurrent.TimeoutException])
def timedRun[F](timeout: Long)(f: => F): F = {

  import java.util.concurrent.{Callable, FutureTask, TimeUnit}

  val task = new FutureTask(new Callable[F]() {
    def call() = f
  })

  new Thread(task).start() 

  task.get(timeout, TimeUnit.MILLISECONDS)
}
3 голосов
/ 04 октября 2011

Только идея: я не очень знаком с akka futures .Но, возможно, можно привязать будущий исполняющий поток к текущему и использовать фьючерсы akka с таймаутами?

2 голосов
/ 04 октября 2011

Насколько мне известно, либо вы выдаете (вычисление вызывает какой-то планировщик) , либо вы используете поток , которым манипулируют извне.

1 голос
/ 05 октября 2011

Для общего решения (без необходимости мусорить каждую из ваших дорогих вычислений с кодом checkTimeout ()), возможно, используйте Javassist.http://www.csg.is.titech.ac.jp/~chiba/javassist/
Затем вы можете динамически вставлять различные методы checkTimeout ().
Вот вступительный текст на их веб-сайте:

Javassist (Java Programming Assistant) делает манипулирование байт-кодом Java простым.Это библиотека классов для редактирования байт-кодов в Java;это позволяет программам Java определять новый класс во время выполнения и изменять файл класса, когда JVM загружает его.В отличие от других подобных редакторов байт-кода, Javassist предоставляет два уровня API: уровень источника и уровень байт-кода.Если пользователи используют API уровня источника, они могут редактировать файл класса без знания спецификаций байт-кода Java.Весь API разработан только со словарем языка Java.Вы даже можете указать вставленный байт-код в виде исходного текста;Javassist компилирует его на лету.С другой стороны, API уровня байт-кода позволяет пользователям напрямую редактировать файл класса как другие редакторы.

Аспектно-ориентированное программирование: Javassist может быть хорошим инструментом для добавления новых методов в класс и для вставки перед/ после / вокруг совета как для вызывающей, так и для вызываемой стороны.

Отражение: Одним из применений Javassist является отражение во время выполнения;Javassist позволяет программам Java использовать метаобъект, который управляет вызовами методов для объектов базового уровня.Никакого специализированного компилятора или виртуальной машины не требуется.

1 голос
/ 04 октября 2011

Я видел такой шаблон хорошо работающий для ограниченных по времени задач (код Java):

try {
    setTimeout(45*60*1000); // 45 min in ms
    while (not done) {
       checkTimeout();
       // do some stuff
       // if the stuff can take long, again:
       checkTimeout();
       // do some more stuff
    }
    return Some(result);
}
catch (TimeoutException ex) {
    return None;
}

Функция checkTimeout() дешево вызывать; Вы добавляете его в код, чтобы он вызывался достаточно часто, но не слишком часто. Все, что он делает - проверяет текущее время по значению таймера, установленному setTimeout() плюс значение времени ожидания Если текущее время превышает это значение, checkTimeout() повышает TimeoutException.

Надеюсь, эту логику можно воспроизвести и в Scala.

1 голос
/ 04 октября 2011

Если вы в порядке с кодом expensiveComputation, чтобы часто проверять Thread.interrupted(), это довольно просто. Но я полагаю, что нет.

Я не думаю, что есть какое-либо решение, которое будет работать для произвольного expensiveComputation кода. Вопрос в том, что вы готовы иметь в качестве ограничения на дорогостоящие вычисления.

У вас есть устаревший и довольно небезопасный Thead.stop(Throwable). Если ваш код не изменяет никаких объектов, кроме тех, которые он создал сам, он может работать.

1 голос
/ 04 октября 2011

Если вы хотите запустить задачу в текущем потоке, и если не должно быть никаких других потоков, вам нужно будет проверить, не истек ли лимит времени внутри expensiveComputation.Например, если expensiveComputation является циклом, вы можете проверить время после каждой итерации.

0 голосов
/ 04 октября 2011

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

Возможно, вы можете использовать аннотацию, например @interruptible, для обозначения методов для улучшения.

0 голосов
/ 04 октября 2011

в текущей теме ?? Phhhew ... Проверять после каждого шага в вычислении Хорошо, если ваши «дорогие вычисления» можно разбить на несколько шагов или использовать итеративную логику, вы можете зафиксировать время, когда вы начинаете, а затем периодически проверять между вашими шагами. Это ни в коем случае не общее решение, но будет работать.

Для более общего решения вы можете использовать аспекты или обработку аннотаций, которые автоматически засоряют ваш код этими проверками. Если «чек» говорит вам, что ваше время истекло, верните None.

Ниже я расскажу о решении в Java, используя аннотации и процессор аннотаций ...

public abstract Answer{}
public class Some extends Answer {public Answer(double answer){answer=answer}Double answer = null;}
public class None extends Answer {}


//This is the method before annotation processing
@TimeLimit(45)
public Answer CalculateQuestionToAnswerOf42() {
 double fairydust = Math.Pi * 1.618;
 double moonshadowdrops = (222.21) ^5;
 double thedevil == 222*3;
 return new Answer(fairydust + moonshadowdrops + thedevil);
}

//After annotation processing
public Answer calculateQuestionToAnswerOf42() {
 Date start = new Date() // added via annotation processing;
 double fairydust = Math.Pi * 1.618; 
 if(checkTimeout(start, 45)) return None; // added via annotation processing;
 double moonshadowdrops = (222.21) ^5;
 if(checkTimeout(start, 45)) return None; // added via annotation processing;
 double thedevil == 222*3;
 if(checkTimeout(start, 45)) return None; // added via annotation processing;
 return new Answer(fairydust + moonshadowdrops + thedevil);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...