On 6/16/07, Trans <transfire / gmail.com> wrote: > Thanks Erwin, that helps. I was toying with not having any methd > arguments. It's interesting, in that it can be done. However, in a > case like the above it requires intantiaing a new object for dealing > with type. > > class X > ... > def go > @ary.each { |x| > TypeRunner.new(x).run > } > end > end > > class TypeRunner > ... > end > > X.new(['a', 'b', 'c']).go > > Am I right about this being thread safe(r) where the first version was > not? Yes, this is thread safe... each thread that calls #go gets its own set of objects (TypeRunners) that are separate from the objects in the other threads. > Also, I was thinking. If it's thread safe to just pass type as an > argument, one would think there could be a way to define a "locked" > instance var that is essentially the same thing but without all the > overhead. It seems like it should be simple, but there's a lot of complexity when programming multithreaded code. In your example you're not changing the state of anything, so that's why you can simplify things... but things get out of hand fast. See my comments past the end of the code below. Here's my attempt at a very *simple* way to do what you're talking about... I had trouble getting the methods put in the right namespace, so maybe my module code isn't the most elegant but it does what I want it to do. require 'thread' module Locker def self.included mod (class << mod; self; end).instance_eval do define_method(:attr_locked) do |*names| class_eval do @@locks ||= {} names.each do |name| # make a lock for each attribute @@locks[name] = Mutex.new # getter define_method(name) do @@locks[name].synchronize do instance_variable_get("@#{name}") end end # setter define_method("#{name}=") do |value| @@locks[name].synchronize do instance_variable_set("@#{name}", value) sleep 5 end end end end # class_eval end # attr_locked end # instance_eval end end class DooWop include Locker attr_locked :chord, :name def initialize name, chord @name, @chord = name, chord end end x = DooWop.new('The Platters', 'Fmaj6/9') $stdout.sync = true def tprint string print "#{Thread.current} #{string}\n" end Thread.new{ tprint "1. #{x.name}" } # this will block access to x.name for 5 seconds Thread.new{ tprint "2. setting name"; x.name = 'The Orioles' } # this will wait for the above thread to finish Thread.new{ sleep 1; tprint "3. #{x.name}" } # this isn't blocked Thread.new{ tprint "4. #{x.chord}" } # this will block access to x.chord for 5 seconds Thread.new do tprint "5. setting chord" x.chord = 'Dm7b5' tprint "5. #{x.chord}" end # this could be indeterminite, we didn't wait for the writer to finish Thread.new{ tprint "6. unsafe: #{x.instance_eval{@name}}" } sleep 0.5 until Thread.list.size == 1 # should be joining here instead This is helpful only in the simplest cases. First it blocks everyone while in the getter or setter... ideally we shouldn't make the getters wait for the other getters to finish. Next, if you try to read the variable more than once in a method, you can still end up with different values. You'd need to wrap the section of code that involves the var in a #synchronize block... but wrap the *least* amount of code necessary because you want to hurry up and let the other threads do their work. There's no way to "automate" that. The code above is only useful in the most simple applications. Further, we've only been talking about one variable at a time. Often you'll need to lock several variables, like maybe we are going to look up on Google which songs by @name have the chord @chord. We'll need to get those values at the *same time* because if we read chord, (another thread interrupts here and changes @name), then read name... that's a problem, and our "locked" variables can't do anything to help. There was a thread on this list a few days ago with a title like "synchronized attr", I only skimmed it but I think they were talking about why you can't really boil thread synchronization down to a one-size-fits-all solution. There are a few data structures (Monitor, ConditionVariable, Queue, etc) that really help out, but there's no way to just call some method to magically make your code thread safe... yet? Regards, Erwin