Ruby同步:如何使线程以正确的顺序一个接一个地工作? [英] Ruby synchronisation: How to make threads work one after another in proper order?

查看:101
本文介绍了Ruby同步:如何使线程以正确的顺序一个接一个地工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的问题是我不知道如何使用Ruby同步多个线程.该任务是创建六个线程并立即启动它们.他们都应该按我需要的顺序依次进行一些工作(例如puts "Thread 1" Hi").

My problem is that I don't know how synchronise multiple threads using Ruby. The task is to create six threads and start them immediately. All of them should do some work (for example puts "Thread 1" Hi") one after another in the order I need it to work.

经过一段时间与Mutex和Condition Variable的斗争之后,我实现了我的目标. 这段代码有点混乱,我故意不使用循环来获得更清晰的视图".

After some time of struggling with Mutex and Condition Variable I've achieved my goal. This code is a little bit messy, and I intentionally did't use cycles for "clearer view".

cv = ConditionVariable.new
mutex = Mutex.new

mutex2 = Mutex.new
cv2 = ConditionVariable.new

mutex3 = Mutex.new
cv3 = ConditionVariable.new

mutex4 = Mutex.new
cv4 = ConditionVariable.new

mutex5 = Mutex.new
cv5 = ConditionVariable.new

mutex6 = Mutex.new
cv6 = ConditionVariable.new



Thread.new do
  mutex.synchronize {
    puts 'First: Hi'
    cv.wait(mutex)
    puts 'First: Bye'
    #cv.wait(mutex)
    cv.signal
    puts 'First: One more time'
  }

end

Thread.new do
  mutex.synchronize {
    puts 'Second: Hi'
    cv.signal
    cv.wait(mutex)
    puts 'Second:Bye'
    cv.signal
  }

  mutex2.synchronize {
    puts  'Second: Starting third'
    cv2.signal

  }
end

Thread.new do
  mutex2.synchronize {
    cv2.wait(mutex2)
    puts 'Third: Hi'
  }

  mutex3.synchronize {
    puts 'Third: Starting forth'
    cv3.signal
  }
end

Thread.new do
  mutex3.synchronize {
    cv3.wait(mutex3)
    puts 'Forth: Hi'
  }

  mutex4.synchronize {
    puts 'Forth: Starting fifth'
    cv4.signal
  }
end

Thread.new do
  mutex4.synchronize {
    cv4.wait(mutex4)
    puts 'Fifth: Hi'
  }

  mutex5.synchronize {
    puts 'Fifth: Starting sixth'
    cv5.signal
  }
end

Thread.new {
  mutex5.synchronize {
    cv5.wait(mutex5)
    puts 'Sixth:Hi'
  }
}

sleep 2

推荐答案

将队列用作PV信号量

您可以使用以下方法滥用队列就像传统的 PV信号.为此,您创建一个Queue实例:

Using Queue as a PV Semaphore

You can abuse Queue, using it like a traditional PV Semaphore. To do this, you create an instance of Queue:

require 'thread'
...
sem = Queue.new

当线程需要等待时,它调用队列#deq :

When a thread needs to wait, it calls Queue#deq:

# waiting thread
sem.deq

当其他某个线程想要取消阻塞正在等待的线程时,它将某些东西(任何东西)推入队列:

When some other thread wants to unblock the waiting thread, it pushes something (anything) onto the queue:

# another thread that wants to unblock the waiting thread
sem.enq :go

工人阶级

这是一个使用Queue同步其开始和结束的工作程序类:

A Worker class

Here's a worker class that uses Queue to synchronize its start and stop:

class Worker

  def initialize(worker_number)  
    @start = Queue.new
    Thread.new do
      @start.deq
      puts "Thread #{worker_number}"
      @when_done.call
    end
  end

  def start
    @start.enq :start
  end

  def when_done(&block)
    @when_done = block
  end

end

构造时,一个工作程序创建一个线程,但是该线程然后在@start队列上等待.直到调用#start,线程才会解除阻塞.

When constructed, a worker creates a thread, but that thread then waits on the @start queue. Not until #start is called will the thread unblock.

完成后,线程将执行被#when_done调用的块.稍后,我们将看到如何使用它.

When done, the thread will execute the block that was called to #when_done. We'll see how this is used in just a moment.

首先,让我们确保如果有任何线程引发异常,我们将对其进行查找:

First, let's make sure that if any threads raise an exception, we get to find out about it:

Thread.abort_on_exception = true

我们需要六个工人:

workers = (1..6).map { |i| Worker.new(i) }

告诉每个工人完成后该怎么做

这是#when_done起作用的地方:

Telling each worker what to do when it's done

Here's where #when_done comes into play:

workers.each_cons(2) do |w1, w2|
  w1.when_done { w2.start }
end

这将轮流每对工人.每个工作人员(最后一个工作人员除外)都被告知,完成工作后,应在工作人员之后启动工作人员.剩下的只有最后一个工人.完成后,我们希望它通知该线程:

This takes each pair of workers in turn. Each worker except the last is told, that when it finishes, it should start the worker after it. That just leaves the last worker. When it finishes, we want it to notify this thread:

all_done = Queue.new
workers.last.when_done { all_done.enq :done }

走吧!

现在剩下的就是启动第一个线程:

Let's Go!

Now all that remains is to start the first thread:

workers.first.start

并等待最后一个线程完成:

and wait for the last thread to finish:

all_done.deq

输出:

Thread 1
Thread 2
Thread 3
Thread 4
Thread 5
Thread 6

这篇关于Ruby同步:如何使线程以正确的顺序一个接一个地工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆