I’d be glad to see a short summary of what I should be aware of when writing
multithreading apps. I know I shouldn’t mix threads with the use of
select(2), but that was greatly explained by matz and others recently. I
also have read about druby requirements for synchronization, so this is
clear. Especially I’d like to know if the regexp related global variables
(and other global vars like $!) are holding a global state that is thread
unsafe. The similar question applies to standard libs - eg. does Kernel#rand
hold an unsafe global state? (I know of scheduled `whrandom’ or whatever its
name is, but I consider standard 1.6.8 stuff for now). What else might be
danger, I don’t know… Could you share your experience in this matter?
I still use Ruby 1.6.8, but it’d be nice to know if such inconsistencies (if
any) are going to be changed in 1.8. Btw, what’s the hypothetical Ruby
version number for planned rewrite of threads layer using native ones?
TIA
···
–
We demand rigidly defined areas of doubt and uncertainty!
While you’re asking that, I have a similar question: is the following
thread-safe? (This is part of PeerTalk, a LAN chat program I’m
writing.)
···
=begin
defer.rb - the Defer module
Defer is used to defer the execution of a block of code, to simplify
multi-threaded programming. Defer is useful when a GUI thread must be
the only thread that interacts with the toolkit, but other threads may
make demands on the GUI at any time. Defer can be mixed in to the GUI
class.
To defer a block, call defer with the block. This can be done from
any thread, and defer will return immediately. When you are ready to
run the blocks, call run_deferred, which will execute the blocks in
the context of the calling thread (not the thread which defered them).
If a deferred block defers a block, that block will be executed
immediately, and the callback won’t be called.
To be notified when a block is deferred, call set_defer_callback with
an associated block. This block will be called whenever a block is
deferred, in the context of the thread defering it. You can use this
to notify the GUI thread that calling run_deferred is necessary.
=end
require “thread”
module Defer
def initialize
super @deferBlocks = Queue.new @deferRunning = false @deferCallback = Proc.new { } @deferMutex = Mutex.new
end
def defer
if @deferRunning
yield
else @deferBlocks.push Proc.new @deferCallback.call
end
end
def run_deferred
return if @deferRunning @deferMutex.synchronize do @deferRunning = true
begin
until @deferBlocks.empty? @deferBlocks.pop.call
end
ensure @deferRunning = false
end
end
end
def set_defer_callback @deferCallback = Proc.new
end
end
class Deferrer
include Defer
def initialize(*args)
super(*args)
end
end
–
Tom Felker
If we developed a replicator, it would be banned for its threat to
the profits of existing business.
First, accesses to @deferRunning should be done synchronized. But: Why do
you introduce @deferRunning when you have already a Mutex? You can do
try_lock and locked? on a Mutex thread safe but without blocking the
current thread.
And: I have the feeling that in defer() a “not” is missing. Shouldn’t it
read
While you’re asking that, I have a similar question: is the following
thread-safe? (This is part of PeerTalk, a LAN chat program I’m
writing.)
=begin
defer.rb - the Defer module
Defer is used to defer the execution of a block of code, to simplify
multi-threaded programming. Defer is useful when a GUI thread must be
the only thread that interacts with the toolkit, but other threads may
make demands on the GUI at any time. Defer can be mixed in to the GUI
class.
To defer a block, call defer with the block. This can be done from
any thread, and defer will return immediately. When you are ready to
run the blocks, call run_deferred, which will execute the blocks in
the context of the calling thread (not the thread which defered them).
If a deferred block defers a block, that block will be executed
immediately, and the callback won’t be called.
To be notified when a block is deferred, call set_defer_callback with
an associated block. This block will be called whenever a block is
deferred, in the context of the thread defering it. You can use this
to notify the GUI thread that calling run_deferred is necessary.
=end
require “thread”
module Defer
def initialize
super @deferBlocks = Queue.new @deferRunning = false @deferCallback = Proc.new { } @deferMutex = Mutex.new
end
def defer
if @deferRunning
yield
else @deferBlocks.push Proc.new @deferCallback.call
end
end
def run_deferred
return if @deferRunning @deferMutex.synchronize do @deferRunning = true
begin
until @deferBlocks.empty? @deferBlocks.pop.call
end
ensure @deferRunning = false
end
end
end
def set_defer_callback @deferCallback = Proc.new
end
end
class Deferrer
include Defer
def initialize(*args)
super(*args)
end
end
–
Tom Felker
If we developed a replicator, it would be banned for its threat to
the profits of existing business.
First, accesses to @deferRunning should be done synchronized. But: Why do
you introduce @deferRunning when you have already a Mutex? You can do
try_lock and locked? on a Mutex thread safe but without blocking the
current thread.
Looking back, I can’t remember why I even have that. If you remove all
reference to @deferRunning, it’ll work the way I want.
And: I have the feeling that in defer() a “not” is missing. Shouldn’t it
read
def defer
if not @deferRunning
…
else
…
end
end
Well, it did work, but you’re right, @deferRunning is pointless. Code
always looks different when you look at it a week later.
I guess my question is, is it OK to pass blocks between threads like
this? Also, am I doing the right thing with making it both a module and
a class?
“Tom Felker” tcfelker@mtco.com schrieb im Newsbeitrag
news:1054670835.2342.74.camel@tom…
First, accesses to @deferRunning should be done synchronized. But: Why
do
you introduce @deferRunning when you have already a Mutex? You can do
try_lock and locked? on a Mutex thread safe but without blocking the
current thread.
Looking back, I can’t remember why I even have that. If you remove all
reference to @deferRunning, it’ll work the way I want.
Well, it did work, but you’re right, @deferRunning is pointless. Code
always looks different when you look at it a week later.
I guess my question is, is it OK to pass blocks between threads like
this?
Yes. After all, a block is an object, isn’t it? And it does not contain
runtime information like a continuation does. So there’s never danger to
get threads mixed up. I’m not sure about continuations. Could be, that
you can’t pass them around.
Also, am I doing the right thing with making it both a module and
a class?