duilio
Moderador
Mensajes: 118
Registrado: 5/6/2004
Estado: Desconectado
|
enviado el
23/10/2004 a las 00:27 |
[Para
quienes no sepan programación funcional por favor no lean lo que
sigue]
Para quienes quieran hacer programas concurrentes en Haskell, es
importante tener en cuenta las limitaciones de cada
implementación. Hugs provee concurrencia cooperativa, es decir
que un programa como:
module Main(main) where
import Control.Concurrent
main :: IO ()
main = forkIO (loop 'a') >> loop 'z'
where loop ch = putChar ch >> loop ch
en Hugs imprimirá una lista infinita de z's. Si no queremos
dejar uno de los threads en starvation, debemos hacer que cooperen,
como en:
module Main(main) where
import Control.Concurrent
niceLoop ch = putChar ch >> yield >> niceLoop ch
main :: IO ()
main = forkIO (niceLoop 'a') >> niceLoop 'z'
ahora se imprimen alternadamente a's y z's. Con GHC esto es distinto,
ya que el modelo de concurrencia allí es preemptivo.
Una cosa a aclarar es que los threads creados con forkIO son corrutinas
en el runtime de la implementación de Haskell particular, no son
procesos ni threads a nivel de SO. Si éso es lo que se quiere,
se debe utilizar forkOS (no disponible en Hugs, en GHC se debe usar el
flag -threaded al enlazar) para crear threads con la biblioteca
disponible en el sistema; o forkProcess del módulo System.Posix
para crear un nuevo proceso.
Saludos,
Duilio.
|
|
|
duilio
Moderador
Mensajes: 118
Registrado: 5/6/2004
Estado: Desconectado
|
enviado el 24/10/2004 a las 21:41 |
Aquí
va un ejemplo mas interesante que el anterior (aunque nada original, el
típico servidor de eco).
-- Descomentar la l'inea siguiente si se va a correr
-- como un script ejecutable (Hugs).
-- #!/usr/bin/runhugs +l
module Main(main) where
import Network.Socket
import Control.Concurrent
main :: IO ()
main = withSocketsDo $ -- solo necesario en Windows, pero por
portabilidad...
do {
-- socket :: Family -> SocketType -> ProtocolNumber -> IO
Socket
sock <- socket AF_INET Stream 0;
-- bindSocket :: Socket -> SockAddr -> IO ()
--
-- iNADDR_ANY es una constante predefinida.
-- Si quiero una direcci'on espec'ifica la puedo
-- construir con inet_addr :: String -> IO HostAddress
bindSocket sock (SockAddrInet (PortNum 5000) iNADDR_ANY);
-- listen :: Socket -> Int -> IO ()
listen sock 5;
-- acceptConnections :: Socket -> IO a
acceptConnections sock;
}
acceptConnections sock = do {
-- accept :: Socket -> IO (Socket, SockAddr)
(newSock, peerAddr) <- accept sock;
forkIO (manageConnection newSock);
acceptConnections sock;
}
manageConnection sock = do {
-- recv :: Socket -> Int -> IO String
s <- recv sock 1024;
putStr ("Received: " ++ s);
send sock s;
sClose sock
}
Éste programa tiene varios problemitas, sugerencias?
Piensen cómo se manejan las señales, cómo se
bifurca la salida con forkIO (en acceptConnections estamos aplicando un
operador monádico dentro de otra mónada, pues la
notación 'do' solo es syntax sugar para >> y 'return').
Saludos,
Duilio.
|
|
Julian_G
Forista habitué
Mensajes: 49
Registrado: 25/5/2004
Estado: Desconectado
|
enviado el 28/10/2004 a las 15:48 |
Duilio,
en acceptConnections, soltando el thread con yield abajo de FokIO se
arregla?
Ahora, como funciona ForkIO ??
No tendria que no ser necesario el yield??
Nos vemos. J.
|
|
duilio
Moderador
Mensajes: 118
Registrado: 5/6/2004
Estado: Desconectado
|
enviado el 31/10/2004 a las 13:05 |
Exacto,
el primer problema que tiene mi programa es que no funciona . Que vos
lo hayas descubierto significa que lo probaste. Moraleja: la mejor
forma de aprender a programar, es programando.
El forkIO duplica el cómputo que venía haciendo la
mónada IO. Se acuerdan de C:
if (fork()) {
/* estoy en el padre */
} else {
/* estoy en el hijo */
}
suponiendo que el fork no falla (!= -1). Según el texto fuente
tengo un solo programa, pero en realidad en cada bloque C voy a tener
un programa ejecutándose como un proceso diferente del otro. Con
forkIO pasa algo semejante, si yo combino con el combinador
monádico:
forkIO f >> g
ahora las funciones f y g se evalúan en 'procesos' diferentes
(aunque en nuestro programa las mónadas son IO, es decir que lo
único que se bifurcará será la entrada salida, y
NADA MÁS).
Saludos,
Duilio.
[Editado el 31/10/2004 por duilio]
|
|
Julian_G
Forista habitué
Mensajes: 49
Registrado: 25/5/2004
Estado: Desconectado
|
enviado el 31/10/2004 a las 17:07 |
Parece
que en Hugs, el modelo es cooperativo (no lei el primer post !! ),
supongo que por eso despues de ForkIO, un Yield funciona.
Mi proceso (el del serverSocket) esta esperando una llamada explicita a
un cambio de contexto. Ahora lo veo.
Bueno, nos vemos! J.
|
|