2013-10-08 71 views
0

我创建了一个使用mplayer播放音乐的脚本。我试图让它工作的方式是允许多个线程(通过HTTP请求)可以播放歌曲。不过,我不想同时播放多首歌曲,而且我也不想排队。我只想播放最近的请求。不过,我遇到了问题,并带有我的互斥体。当我预期它不会总是返回锁定状态。Ruby中的子进程和互斥体

Class Handler 

    def initialize 
     @read_io, @write_io = IO.pipe 
     @child = nil 
     @mutex = Mutex.new 
    end 

    def play_song_with_id(id) 
     if @mutex.locked? then # this doesn't always return as expected 
      stop_mplayer # this is how I interupt the child 
      @mutex.unlock 
     end 
     if @mutex.lock then 
      @child = fork do 
       STDIN.reopen(@read_io) 
       `mplayer -really-quiet "#{id}"` 
       exit 
      end 
      Process.detatch(@child) 
     end 
    end 

    def stop_mplayer() 
     @write_io.write "q" # mplayer takes input 'q' to quit 
    end 

end 

只是为了给出完整的图片,下面是我如何路由请求。一个简单的WEBrick服务器:

if $0 == __FILE__ then 

    # Create the server 
    server = WEBrick::HTTPServer.new(:Port=>port) 
    ip = IPSocket.getaddress(Socket.gethostname) 

    # Create a handler 
    handler = Handler.new 

    # Define routes 
    server.mount "/handle", Routes::HandleRoute, handler 

    # Handle interuptions 
    trap "INT" do 
     server.shutdown 
    end 

    # Start the server 
    puts "\n====================" 
    puts " * Server running at #{ip} on port #{port}" 
    puts "====================\n\n" 

    server.start 
end 

和路由:

class HandleRoute < WEBrick::HTTPServlet::AbstractServlet 

    def initialize server, handler 
     @handler = handler 
    end 

    def do_POST(request, response) 
     if(request.body) 
      @handler.play_song_with_id(request.body) 
     end 
    end 

end 

TL; DR - 不知何故,有时两首歌曲将发挥在一次,我想使用@mutex防止。我想播放最近的请求,并停止正在发生的任何播放。我想知道我试图停止播放的方式是问题,而不是互斥?如果是这样,那么打破孩子的更好方法是什么?

回答

2

我会尽量简化你对互斥量的使用。您的当前实现看起来容易受到解锁和再次锁定互斥锁之间的时间问题的影响。

class Handler 
    def play_song_with_id(id) 
    @mutex.synchronize do 
     stop_mplayer 
     @child = fork do 
      STDIN.reopen(@read_io) 
      `mplayer -really-quiet "#{id}"` 
      exit 
     end 
     Process.detach(@child) 
    end 
    end 
end 
+0

这个简化的方法似乎已经成功了。谢谢! – hodgesmr

0

我不认为你在这种情况下真的需要互斥锁。既然你只想玩最新的游戏,那么每当有新的请求完成时你总是可以杀死mplayer。所以:

def play_song_with_id(id) 
    stop_mplayer_if_any 
    start_mplayer 
end 


def stop_mplayer_if_any() 
    @write_io.write "q" 
rescue => e # Please do a better job here, rescuing only the really expected exceptions 
    logger.info "Rescuing from #{e.inspect} and ignoring" 
end 

def start_mplayer 
    @child = fork do 
    STDIN.reopen(@read_io) 
    `mplayer -really-quiet "#{id}"` 
    exit 
    end 
end 

没有同步问题时,不需要它。