hgearman工作者如何工作?

时间:2017-04-01 10:47:50

标签: haskell gearman

我在三周前问了一个问题how does hgearman-client work?。在一些帮助下,我编写了一个简单的客户端应用程序,现在我正在工作方面工作。下面的工作器实现编译得很好并且没有任何异常地运行。唯一的麻烦是W.runWorker gc (return g)不会被执行。如果我理解正确,那就是Haskell懒惰和return t Monad包装的结果。但我还不知道如何摆脱这个问题。有人可以帮忙吗?

import qualified Control.Monad.State as S
import qualified Data.ByteString.Char8 as B
import qualified Network.Gearman.Client as C
import qualified Network.Gearman.Worker as W
import Network.Gearman.Internal (Function, Port)
import Network.Socket (HostName)

main :: IO ()
main = do
  c <- connect
  case c of
    Left e -> error $ B.unpack e
    Right gc -> do
      (res, _) <- flip S.runStateT gc $ do
        g <- (W.registerWorker name func)
        let t = W.runWorker gc (return g)
        return t >> return ()

      return res
  where
    connect = C.connectGearman (B.pack "i") host port
    host = "localhost"::HostName
    port =  4730::Port
    name = (B.pack "foo")::Function
    func _ = B.pack "bar"

不幸的是,绑定t <- W.runWorker的尝试以编译器异常结束。如果我以这种方式更改代码:

Right gc -> do
  (res, _) <- flip S.runStateT gc $ do
    g <- (W.registerWorker name func)
    t <- W.runWorker gc (return ())
    return t >> return  ()

  return res

编译失败,异常:

Couldn't match expected type `S.StateT
                                Network.Gearman.Internal.GearmanClient IO a0'
                with actual type `IO GHC.Conc.Sync.ThreadId'
    In a stmt of a 'do' block: t <- W.runWorker gc (return ())
    In the second argument of `($)', namely
      `do { g <- (W.registerWorker name func);
            t <- W.runWorker gc (return ());
            return t >> return () }'

IO GHC.Conc.Sync.ThreadIdrunWorker的结果。

2 个答案:

答案 0 :(得分:1)

某些Gearman a类型a的值是操作,是执行某项操作的方法。你可以绑定这些食谱来制作更大的食谱,直到你构建了main食谱,这是一个可以运行的食谱。

实际上,这意味着如果你正在运行一个看起来像这样的do-block:

do ...
   foo
   ...

然后foo将会运行。如果你有一个看起来像这样的do-block:

do ...
   ret <- foo
   ...

然后会运行foo,并且运行foo的结果将存储在ret中。这两个语法是绑定。但是,如果您正在运行看起来像这样的do-block:

do ...
   let ret = foo
   ...

然后foo将无法运行 - 相反,您只是要求变量ret成为foo的简写,因此fooret之后可以互换。

所以现在你可以看到:

do g <- W.registerWorker name func
   let t = W.runWorker gc (return g)
   return t >> return ()

第二行实际上运行一个工作者,它只是让t成为运行工作者的简写。返回动作也不会绑定它。你需要绑定:

   t <- W.runWorker gc (return g)

顺便说一句,我一直在查看文档,看起来registerWorker会返回Gearman (),这意味着运行操作的结果是()或“没什么有趣的”。所以g没什么好玩的,你可以摆脱它并说出

do W.registerWorker name func
   t <- W.runWorker gc (return ())
   return t >> return ()

大概在第二行代替return (),你会想要在工作者中运行一个动作。像:

   t <- W.runWorker gc $ do
       ... the things you want the worker to do ...
   return t >> return ()

最后一行:return t >> return (),也写了

do return t
   return ()

return ()完全相同。 return x构造一个没有副作用的动作,仅用于结果。然后,当您使用>>(或不将结果绑定到do块中)时,您只针对其副作用运行操作并丢弃其结果。所以第一个return什么都不做。

答案 1 :(得分:0)

最后,我在Haskell实施了一名齿轮工人。

{-# LANGUAGE DeriveDataTypeable #-}
import Control.Exception (Exception, IOException, catch, throwIO)
import qualified Data.ByteString.Char8 as B
import Control.Monad.State
import Data.Typeable     (Typeable)
import qualified Network.Gearman.Client as C
import qualified Network.Gearman.Worker as W
import Network.Gearman.Internal (Function, GearmanClient, Port)
import Network.Socket (HostName)
import           Control.Concurrent
import qualified Control.Monad.State as S

data ConnectException = ConnectException HostName Port IOException
    deriving (Show, Typeable)
instance Exception ConnectException

main :: IO ()
main = do
  c <- connect
  gc <- either (error . B.unpack) return c
  work gc
  return ()
  where
    connect = C.connectGearman (B.pack "worker-id") host port `catch` \e -> throwIO (ConnectException host port e)
    host = "localhost"::HostName
    port =  4730::Port

work :: GearmanClient -> IO ()
work gc = do
      (res, _) <- flip S.runStateT gc $ do
          W.registerWorker (B.pack "reverse"::Function)  B.reverse
          S.get >>= (\env -> forever $ S.liftIO (W.runWorker env (return ()) >> threadDelay (1000*1000)))
          return ()
      return res