2011-09-15 11 views
5

Observé un comportamiento extraño con la función threadDelay en GHC.Conc en algunas de mis máquinas. El siguiente programa:ThreadDelay Problema en Haskell (GHC) en Ubuntu

main = do print "start" 
      threadDelay (1000 * 1000) 
      print "done" 

tarda 1 segundo en ejecutarse, como se esperaba. Por otra parte, este programa:

{-# LANGUAGE BangPatterns #-} 
import Control.Concurrent 

main = do print "start" 
      loop 1000 
      print "done" 
    where loop :: Int -> IO() 
     loop !n = 
      if n == 0 
      then return() 
      else do threadDelay 1000 
        loop (n-1) 

tarda unos 10 segundos para ejecutarse en dos de mis máquinas, aunque en otras máquinas que tarda aproximadamente 1 segundo, como se esperaba. (He compilado los dos programas anteriores con el indicador '-threaded'). Aquí hay una captura de pantalla de Threadscope que muestra que hay actividad solo una vez cada 10 milisegundos: Screenshot of ThreadScope showing that threadDelay of 1 millisecond sleeps for 10 milliseconds.

Por otro lado, aquí hay una captura de pantalla de ThreadScope de una de mis máquinas en las que toma el programa de 1 segundo en total: Screenshot of ThreadScope showing that threadDelay of 1 millisecond sleeps for about 1 milliseconds.

Un programa C similar:

#include <unistd.h> 
#include <stdio.h> 

int main() { 
    int i; 
    for (i=1; i < 1000; i++) { 
    printf("%i\n",i); 
    usleep(1000); 
    } 
    return 0; 
} 

hace lo correcto, 'tiempo ./a.out', es decir, se ejecuta da salida como :

1 
2 
... 
999 

real 0m1.080s 
user 0m0.000s 
sys 0m0.020s 

¿Alguien ha tenido este problema anteriormente? Si es así, ¿cómo puede solucionarse? Estoy ejecutando ghc 7.2.1 para Linux (x86_64) en todas mis máquinas y estoy ejecutando varias versiones de Ubuntu. Funciona mal en Ubuntu 10.04.2, pero está bien en 11.04.

Respuesta

3

threadDelay no es un temporizador preciso. Promete que su hilo durmirá durante al menos, siempre que su argumento lo indique, pero no promete nada más que eso. Si quiere que algo suceda periódicamente, tendrá que usar algo más. (No estoy seguro de qué, pero posiblemente Unix' realtime alarm signal funcionaría para usted.)

1

Sospecho que olvidó compilar con la opción '-threaded'. (I hizo que una vez a 6.12.3, y constantemente tenía 30 retrasos hilo milisegundos.)

+0

De hecho compilé con la bandera '-threaded'. – Andreas

1

Como se señaló anteriormente, threadDelay sólo tiene una garantía, que es que tienes que esperar al menos siempre y cuando se solicite. El tiempo de ejecución de Haskell no obtiene cooperación especial del OS

Aparte de eso, es el mejor esfuerzo del sistema operativo.

Podría valer la pena comparar sus resultados para threadDelays. Por ejemplo:

module Main where 
import Control.Concurrent 
import Data.Time 

time op = 
    getCurrentTime >>= \ t0 -> 
    op >> 
    getCurrentTime >>= \ tf -> 
    return $! (diffUTCTime tf t0) 

main :: IO() 
main = 
    let action tm = time (threadDelay tm) >>= putStrLn . show in 
    mapM action [2000,5000,10000,20000,30000,40000,50000] >> 
    return() 

En mi caja de ventanas, esto me da:

0.0156098s 
0.0156098s 
0.0156098s 
0.0312196s 
0.0312196s 
0.0468294s 
0.0624392s

Esto sugiere la combinación de demora y getCurrentTime tiene una resolución de 15,6 milisegundos. Cuando doy 1000 vueltas 1000 veces, termino esperando 15.6 segundos, así que este es solo el mínimo de espera para un hilo.

En mi caja de Ubuntu (11.04, con núcleo 2.6.38-11), obtengo una precisión mucho mayor (~ 100us).

Es posible que pueda evitar el problema del tiempo manteniendo el programa más ocupado, por lo que no cambia el contexto. De cualquier manera, le sugiero que no use threadDelay para el tiempo, o al menos verifique la hora y realice cualquier operación hasta el instante dado.

Su sueño de alta precisión a través de C podría funcionar para usted, si está dispuesto a ensuciar con FFI, pero el costo es que tendrá que usar hilos vinculados (al menos para su temporizador).

Cuestiones relacionadas