Represent process as a thread in Ruby - ruby

I'm supposed to implement a very simple version of MPI (or rather simulate the behaviour) in ruby.
Part of the assignment is to create a Communicator class which has
different processes running on different hosts.
To prove the "independency" of the processes two consecutive for loops over the array in which the processes are stored should output them in a different order.
The processes should be represented as either instances of a class I made up or as threads.
For the first part I just created a class which has the required attributes and changed the "each" method to shuffle the process list after each call.
However, I'm stuggling with the second part. For now I've just created a subclass of Thread and added the required attributes and methods. I just create as many instances of my subclass with an infinite loop in the block as needed and store them in an array.
Now, on looping through the array, the processes seem to have different response times but still are written to console in order.
As I'm in doubt to whether my approach is right or not, I would like to have some suggestions on how the required behaviour could be achieved:
for process in communicator write rank
for process in communicator write rank
> 1
> 2
> 3
#####
> 3
> 1
> 2
Edit:
The Communicator:
class ThreadKomunikator
attr_reader :hosty, :procesy, :rankPool
##rankPool = (0..100).to_a.reverse
public
def initialize(hosty)
#procesy = []
#hosty = hosty
hosty.each do |name, number|
for i in 1..number
tempProc = ThreadProc.new{while true do continue; end;}
tempProc.init(getRank(),name)
procesy << tempProc
end
end
end
def MPI_Comm_size()
procesy.length
end
def each
procesy.each do |proces|
yield proces
end
# #procesy.shuffle!
end
def [](x)
procesy.each do |proces|
if proces.rank == x
return proces.MPI_Get_processor_name()
end
end
return "No process of that rank found!"
end
def method_missing(method, *args, &block)
if hosty.has_key?("#{method}")
return hosty["#{method}"]
else
return 0
end
end
private
def getRank
##rankPool.pop
end
end
The subclass of Thread:
class ThreadProc< Thread
attr_accessor :rank, :host
def init(rank, host)
#rank = rank
#host = host
end
def MPI_Comm_rank()
#rank
end
def MPI_Get_processor_name()
#host
end
end

Related

Learning Ruby threading - trigger an event when thread finishes

I'm new to multi-threading and I'm looking for some help understanding the idiomatic way of doing something when a thread is finished, such as updating a progress bar. In the following example, I have several lists of items and routines to do some "parsing" of each item. I plan to have a progress bar for each list so I'd like to be able to have each list's parsing routine update the percentage of items completed. The only "trigger" point I see is at the puts statement at the end of an item's sleepy method (the method being threaded). What's the generally accepted strategy for capturing the completion, especially when the scope of the action is outside the method running in the thread?
Thanks!
# frozen_string_literal: true
require 'concurrent'
$stdout.sync = true
class TheList
attr_reader :items
def initialize(list_id, n_items)
#id = list_id
#items = []
n_items.times { |n| #items << Item.new(#id, n) }
end
def parse_list(pool)
#items.each do |item|
pool.post { item.sleepy(rand(3..8)) }
end
end
end
class Item
attr_reader :id
def initialize (list_id, item_id)
#id = item_id
#list_id = list_id
end
def sleepy(seconds)
sleep(seconds)
# This puts statement signifies the end of the method threaded
puts "List ID: #{#list_id} item ID:#{#id} slept for #{seconds} seconds"
end
end
lists = []
5.times do |i|
lists << TheList.new(i, rand(5..10))
end
pool = Concurrent::FixedThreadPool.new(Concurrent.processor_count)
lists.each do |list|
list.parse_list(pool)
end
pool.shutdown
pool.wait_for_termination
The issue isn't really about "knowing when the thread finished", but rather, how can you update a shared progress bar without race conditions.
To explain the problem: say you had a central ThreadList#progress_var variable, and as the last line of each thread, you incremented it with +=. This would introduce a race condition because two threads can perform the operation at the same time (and could overwrite each other's results).
To get around this, the typical approach is to use a Mutex which is an essential concept to understand if you're learning multithreading.
The actual implementation isn't that difficult:
require 'mutex'
class ThreadList
def initialize
#semaphore = Mutex.new
#progress_bar = 0
end
def increment_progress_bar(amount)
#semaphore.synchronize do
#progress_bar += amount
end
end
end
Because of that #semaphore.synchronize block, you can now safely call this increment_progress_bar method from threads, without the risk of race condition.

Ruby: How do I share a global variable amongst threads that are running an object.method

I am running a ruby script that creates several threads.
I want to make the threads have access to a common variable that lets the main thread know when to join the threads.
I am trying to do this with a $global variable, but the threads don't seem the be able to access the $global.
class IO_
def change(number)
sleep(60 * number)
$trade_executed = true
end
end
io = IO_.new
numbers = 1, 2
$threads = {}
$trade_executed = false
def start_threads(numbers)
numbers.each do |number|
$threads[number] = Thread.new {io.change(number)}
end
end
start_threads(numbers)
while true
p $trade_executed
p $threads
sleep(10)
end
The above $trade_executed will always be 'false'.
But if I move the method change outside of the io object it works.
The problem is in the function start_threads. You called io.change(number) in that function, but the local variable io is not defined in that function. The consequence is that both threads died due to NameError.
You can change the start_threads function as this:
def start_threads(numbers, io)
numbers.each do |number|
$threads[number] = Thread.new {io.change(number)}
end
end
and call it like this:
start_threads(numbers, io)

Making a Yhatzee game, array won't show up on screen

Ok so I just started learning ruby and I'm making a Yhatzee game, now this is where I'm currently at:
class Yhatzee
def dices
#dices.to_a= [
dice1=rand(1..6),
dice2=rand(1..6),
dice3=rand(1..6),
dice4=rand(1..6),
dice5=rand(1..6)
]
end
def roll_dice
#dices.to_a.each do |dice|
puts dice
end
end
end
x = Yhatzee.new
puts x.roll_dice
Now the reason i typed .to_a after the array is i kept getting a "uninitialized variable #dices" error, and that seemed to fix it, i have no idea why.
anyways on to my question, i currently don't get any errors but my program still won't print anything to the screen. I expected it to print out the value of each dice in the array... any idea what I'm doing wrong? It seems to work when i do it in a procedural style without using classes or methods so i assumed it might work if i made the 'dices' method public. But no luck.
There are a few issues here. Firstly #dices is nil because it is not set anywhere. Thus when you call #dices.to_a you will get []. Also the dices method will not work either because nil does not have a to_a= method and the local variables you are assigning in the array will be ignored.
It seems a little reading is in order but I would do something like the following: (Not the whole game just refactor of your code)
class Yhatzee
def dice
#dice = Array.new(5){rand(1..6)}
end
def roll_dice
puts dice
end
end
x = Yhatzee.new
puts x.roll_dice
There are alot of additional considerations that need to be made here but this should at least get you started. Small Example of how I would recommend expanding your logic: (I did not handle many scenarios here so don't copy paste. Just wanted to give you a more in depth look)
require 'forwardable'
module Yahtzee
module Display
def show_with_index(arr)
print arr.each_index.to_a
print "\n"
print arr
end
end
class Roll
include Display
extend Forwardable
def_delegator :#dice, :values_at
attr_reader :dice
def initialize(dice=5)
#dice = Array.new(dice){rand(1..6)}
end
def show
show_with_index(#dice)
end
end
class Turn
class << self
def start
t = Turn.new
t.show
t
end
end
attr_reader :rolls
include Display
def initialize
#roll = Roll.new
#rolls = 1
#kept = []
end
def show
#roll.show
end
def roll_again
if available_rolls_and_dice
#rolls += 1
#roll = Roll.new(5-#kept.count)
puts "Hand => #{#kept.inspect}"
show
else
puts "No Rolls left" if #rolls == 3
puts "Remove a Die to keep rolling" if #kept.count == 5
show_hand
end
end
def keep(*indices)
#kept += #roll.values_at(*indices)
end
def show_hand
show_with_index(#kept)
end
def remove(*indices)
indices.each do |idx|
#kept.delete_at(idx)
end
show_hand
end
private
def available_rolls_and_dice
#rolls < 3 && #kept.count < 5
end
end
end
The main problem with this code is that you are trying to use the #dices instance variable inside of the roll_dice method, however you are not defining the instance variable anywhere (anywhere that is being used). You have created the dices method but you are not actually instantiating it anywhere. I have outlined a fix below:
class Yhatzee
def initialize
create_dices
end
def roll_dice
#dices.each do |dice|
puts dice
end
end
private
def create_dices
#dices = Array.new(5){rand(1..6)}
end
end
x = Yhatzee.new
x.roll_dice
I have done some simple refactoring:
Created an initialize method, which creates the #dice instance variable on the class initialization.
Made the 'dices' method more descriptive and changed the method visibility to private so only the class itself is able to create the #dice.
Cleaned up the creation of the dices inside of the #dice instance variable
I have omitted the .to_a from the roll_dice method, now that we create the variable from within the class and we know that it is an array and it will be unless we explicitly redefine it.
UPDATE
Although I cleaned up the implementation of the class, it was kindly pointed out by #engineersmnky that I oversaw that the roll would return the same results each time I called the roll_dice function, I have therefore written two functions which will achieve this, one that defines an instance variable for later use and one that literally just returns the results.
class Yhatzee
def roll_dice
#dice = Array.new(5){rand(1..6)} # You will have access to this in other methods defined on the class
#dice.each {|dice| puts dice }
end
def roll_dice_two
Array.new(5){rand(1..6)}.each {|dice| puts dice } # This will return the results but will not be stored for later use
end
end
x = Yhatzee.new
x.roll_dice
x.roll_dice # Will now return a new result

Proper Mutex usage / Good coding style?

Within the following code, the producer periodically_fill_page_queue might add a page to the queue that is currently being consumed (read: in the consumer before the status being_processed is set).
class Example
def initialize
#threads = ThreadGroup.new
#page_queue = Queue.new
Thread.abort_on_exception = true
end
def start
periodically_fill_page_queue
periodically_process_page_queue
end
def periodically_fill_page_queue
#threads.add(Thread.new do
loop do
if #page_queue.empty?
Page.with_state(:waiting).each do |p|
p.queued!
#page_queue << f
end
end
sleep 2
end
end)
end
def periodically_process_page_queue
loop do
until file = #page_queue.pop
sleep 2
end
page.being_processed
process(page)
end
end
def process(page)
sleep 120
page.processed
end
end
class Page < ActiveRecord::Base
state_machine :state, :initial => :waiting do
event :queued do
transition :waiting => :queued
end
event :being_processed do
transition :queued => :being_processed
end
event :processed do
transition :being_processed => :processed
end
end
end
To avoid this, i'd use a Mutex object:
def initialize
...
#mutex = Mutex.new
end
def periodically_process_page_queue
loop do
until file = #page_queue.pop
sleep 2
end
#mutex.synchronize { page.being_processed }
process(page)
end
end
Is this "good" coding style, or are there any more elegant approaches?
Thanks!
Not with this design. Some alternative designs might do one of the below, but naturally have their own can of worms.
"Fork"
For each job you start a new thread or process, giving it the job
Delegation
Delegate the task to a queue in each thread. Each thread pulls from its own unique queue.
Stride
You have a circular buffer and each thread checks at a different interval. E.G. Num_threads + thread.id
This probably isn't for your situation.
Range
A thread is responsible for a range of jobs. num_threads * thread.id
This probably isn't for your situation.

How to restrict the number of objects of a class to a given number in ruby?

how can i extend the singleton pattern to a number of objects for a class i.e. how can i have only 5 objects of a class at max and not more than that in ruby
Example code:
# Example class which can be instanciated at most five times
# Naive approach with Class variable
class FiveAtMost
##instances = 0
def initialize()
if ##instances >= 5
raise "No more than five instances allowed."
else
##instances += 1
end
p "Initialized instance #{##instances}"
end
end
one = FiveAtMost.new
two = FiveAtMost.new
three = FiveAtMost.new
four = FiveAtMost.new
five = FiveAtMost.new
# will raise RuntimeError: No more than five instances allowed.
six = FiveAtMost.new
Since the moment when an object gets garbage collected is not predictable, you'll need some kind of workaround for the that event. Maybe you find this useful: http://pleac.sourceforge.net/pleac_ruby/classesetc.html#AEN710
Multiton with size constraints.
Here is a naive implementation of a Multiton module that you can include in any class. The default number of objects it can create is 5, and you can override this limit by defining a class method called max_instances that returns a number (maximum instances allowed) in your class.
module Multiton
MAX_INSTANCES = 5
COUNT_HOOK = :max_instances
module MultitonClassMethods
def instance
size = #instances.size
max = respond_to?(COUNT_HOOK) ? self.send(COUNT_HOOK) : MAX_INSTANCES
#instances << new if size < max
#instances[rand(size)]
end
end
def self.included(klass)
klass.class_eval {
#instances = []
}
klass.private_class_method :new
klass.extend(MultitonClassMethods)
end
end
Include the module in a class to make it a multiton.
# Falls back to Multiton::MAX_INSTANCES
class Person
include Multiton
end
# Overrides the number of allowed instances in max_instances
class Resource
include Multiton
def self.max_instances
58
end
end
Since the objects are returned randomly from a pool in this multiton, you may not get all the objects back over a short run. But as more objects are requested, it should even out. You can change this behavior in the Multitonmodule by cycling through objects instead of randomly picking one.
people = []
1000.times do
people << Person.instance
end
# should print 5, but may print a smaller number
p people.uniq.size
resources = []
1000.times do
resources << Resource.instance
end
# should print 58, but may print a smaller number
p resources.uniq.size

Resources