2013-10-08 69 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 

沒有同步問題時,不需要它。