I have something similar in my project
class Raj
def execute
5.times do
Thread.new do
object = Gopal.new
object.db_connection
object.enter_tax_id
end
end
end
end
class Gopal
def db_connection
#db = "" # Created db connection here
#browser = Watir::Browser.new
end
def enter_tax_id
m = Mutex.new
m.synchronize do
data = #db_conn.select_one("select max(tax_id_no) from pcmp.tax_identifier")
#browser.text_field(id: 'something').set 'data'
end
end
end
The enter tax id method pulls information from the database and then enters a value into the text field. This thread has an issue since other threads are interacting with it; when multiple threads attempt to execute the same procedure, a 'executing in another thread' error is raised.
In order to resolve this issue, I used the following strategy:
class Gopal
#mutex = Mutex.new
def self.mutex_var
#mutex
end
def db_connection
#db = "" # Created db connection here
#browser = Watir::Browser.new
end
def enter_tax_id
Gopal.mutex_var.synchronize do
data = #db.select_one("select max(tax_id)+1 from table")
#browser.text_field(id: 'something').set 'data'
end
end
end
The mutex variable is now persistent as long as the class is loaded, thus even if another object is instantiated, the mutex variable will still be present and provide protection.
Related
class Tour
def destinations
threads = []
[:new_york, :london, :syndey].each { |city|
#threads << Thread.new {
where = city
goto(where)
}
}
threads.each(&:join)
end
def where=(location)
Thread.current[:city] = location
end
def where
Thread.current[:city]
end
def goto(city)
puts "I am going to visit #{city}."
end
end
Tour.new.destinations
In order to access thread local variable in method goto(), the thread local variable has to be passed to it like goto(where), if there are many other similar methods need to do things based upon current thread local variable :city, then it has to be passed to other methods too.
I guess there is an elegant/ruby way to avoid passing thread local variable as an option, what does that look like?
This seems like you'll trip yourself up a lot. It might be better to initialize a new object for each thread.
class Tour
def self.destinations
threads = []
[:new_york, :london, :sydney].each do |city|
threads << Thread.new { Destination.new(city).go }
end
threads.each(&:join)
end
end
class Destination
attr_reader :location
def initialize(location)
#location = location
end
def go
puts "I am going to visit #{location}."
end
end
# Tour.destinations
Suggested reading: https://blog.engineyard.com/2011/a-modern-guide-to-threads
Working in Ruby, I'm getting an error saying
'add': undefined local variable or method 'food' for #<FoodDB:...
This is the code I'm trying to run
require_relative 'FoodDB.rb'
class Manager
def initialize
food = FoodDB.new
self.create_foodDB(food)
end
def create_foodDB(food)
counter = 1
word = []
file = File.new("FoodDB.txt","r")
while (line = file.gets)
food.addFood(line)
counter = counter + 1
end
file.close
end
end
manager = Manager.new
input_stream = $stdin
input_stream.each_line do |line|
line = line.chomp
if line == "quit"
input_stream.close
end
end
This is FoodDB.rb's code
class FoodDB
def initialize
food = []
end
def addFood(str)
food.push(str)
end
end
I'm not sure what's the problem since it seems like I'm definitely calling the correct method from the FoodDB class. All help is appreciated, thank you!
You need to change food in the FoodDB class to an instance variable:
class FoodDB
def initialize
#food = []
end
def addFood(str)
#food.push(str)
end
end
An instance variable will be available throughout all methods of an instance, while the food variable you used was local to its lexical scope, i.e. only available within the initialize method.
I created small API library, everything worked fine, until I realized, that I need multiple configurations.
It looks like this:
module Store
class Api
class << self
attr_accessor :configuration
def configure
self.configuration ||= Configuration.new
yield configuration
end
def get options = {}
url = "#{configuration.domain}/#{options[:resource]}"
# ResClient url ...
end
end
end
class Configuration
attr_accessor :domain
def initialize options = {}
#domain = options[:domain]
end
end
class Product
def self.get
sleep 5
Api.get resource: 'products'
end
end
end
When I run it simultaneously, it override module configuration.
Thread.new do
10.times do
Store::Api.configure do |c|
c.domain = "apple2.com"
end
p Store::Product.get
end
end
10.times do
Store::Api.configure do |c|
c.domain = "apple.com"
end
p Store::Product.get
end
I can't figure out, how make this module better. Thanks for your advise
Well, if you don't want multiple threads to compete for one resource, you shouldn't have made it a singleton. Try moving object configuration from class to its instances, then instantiate and configure them separately.
There is more to refactor here, but this solves your problem:
module Store
class API
attr_reader :domain
def initialize(options = {})
#domain = options[:domain]
end
def products
sleep 5
get resource: 'products'
end
private
def get(options = {})
url = "#{configuration.domain}/#{options[:resource]}"
# ResClient url ...
end
end
end
Thread.new do
10.times do
api = Store::API.new(domain: 'apple2.com')
p api.products
end
end
10.times do
api = Store::API.new(domain: 'apple.com')
p api.products
end
I'm trying to achieve a socket server on Ruby with multiply rooms, where people can send chat messages to each other, for example.
The code is pretty simple:
Main file:
require 'room'
require 'socket'
room = Room.new
### handle connections
Thread::abort_on_exception=true
server = TCPServer.open(2000) # Socket to listen on port 2000
puts 'Open socket at 2000 port'
loop { # Servers run forever
room.handle_player server.accept
}
room.rb
require 'player'
class Room
#players = []
attr_accessor :players
def initialize
end
def handle_player(connection)
puts ' New client connected!'
Thread.start(connection, self) do |client, room|
player = Player.new connection, room
#players.push player
end
end
def broadcast(message)
puts "Players is #{#players.class}" #NilClass !
end
end
player.rb
class Player
def initialize(connection, room)
#room = room
while line = connection.gets
puts 'got line ' + line
room.broadcast line
end
end
end
The problem is that when I call Room#broadcast from a player – it tells me that #players is nil, but it isn't! How that can be?
I'm not sure I'm doing everything correct (my feelings tell me that player should not have direct link to room), but it simplifies the example.
This has nothing to do with threads.
You should put that initialization #players = [] into the initialize of Room.
The way you have it, you do not initialize an instance variable of your object but an instance variable of the class Room.
You set #players as a Room's class instance variable.
Instead of this, you should put this in your initialize method:
def initialize
#players = []
end
The following does not work. The call to resources.next_document within the thread returns nil. The same call without threading works as expected.
Any MongoDB experts out there? :P
resources = db[Resource::COLLECTION].find
number_of_threads.times do
threads << Thread.new do
while resource = resources.next_document
puts 'one more doc'
end
end
end
This is the solution I ended up using:
Feedback welcome
pool = DocumentPool.new(db)
5.times do
Thread.new do
while doc = pool.next_document
#something cool
end
end
end
class DocumentPool
COLLECTION = 'some_collection'
def initialize(db)
#db = db
#first_doc = cursor.next_document
end
def collection
#db[COLLECTION]
end
def cursor
#cursor ||= collection.find
end
def shift
doc = nil
if #first_doc
doc = #first_doc
#first_doc = nil
else
doc = cursor.next_document
end
doc
end
def count
collection.count
end
end
Although the driver itself is threadsafe, individuals cursor aren't, so you can't reliably process the data in the way you're describing.
One possibility would be to have a single thread that iterates over the documents, handing them off to any number of worker threads for the actual processing.