[macruby-changes] [3599] MacRuby/trunk
source_changes at macosforge.org
source_changes at macosforge.org
Wed Feb 24 13:23:20 PST 2010
Revision: 3599
http://trac.macosforge.org/projects/ruby/changeset/3599
Author: ernest.prabhakar at gmail.com
Date: 2010-02-24 13:23:20 -0800 (Wed, 24 Feb 2010)
Log Message:
-----------
Make Dispatch::Job#value non-destructive
Modified Paths:
--------------
MacRuby/trunk/lib/dispatch/README.rdoc
MacRuby/trunk/lib/dispatch/job.rb
MacRuby/trunk/spec/macruby/library/dispatch/job_spec.rb
Modified: MacRuby/trunk/lib/dispatch/README.rdoc
===================================================================
--- MacRuby/trunk/lib/dispatch/README.rdoc 2010-02-24 21:23:11 UTC (rev 3598)
+++ MacRuby/trunk/lib/dispatch/README.rdoc 2010-02-24 21:23:20 UTC (rev 3599)
@@ -50,13 +50,10 @@
This will wait until the value has been calculated, allowing it to be used as an {explicit Future}[http://en.wikipedia.org/wiki/Futures_and_promises]. However, this may stall the main thread indefinitely, which reduces the benefits of concurrency.
-Wherever possible, you should instead attempt to figure out exactly _when_ and _why_ you need to know the results of asynchronous work. Then, call +value+ with a block to also perform _that_ work asynchronously once the value has been calculated -- all without blocking the main thread:
+Wherever possible, you should instead attempt to figure out exactly _when_ and _why_ you need to know the result of asynchronous work. Then, call +value+ with a block to also perform _that_ work asynchronously once the value has been calculated -- all without blocking the main thread:
- job = Dispatch::Job.new { Math.sqrt(10**100) }
job.value {|v| p v.to_int.to_s.size } # => 51 (eventually)
-Note that +value+ removes the value as it is returned, and thus cannot be called multiple times.
-
=== Job#join: Job Completion
If you just want to track completion, you can call +join[http://ruby-doc.org/core/classes/Thread.html#M000462]+, which waits without returning (or removing) the result:
@@ -74,12 +71,21 @@
job.add { Math.sqrt(2**64) }
-If there are multiple blocks in a job, +value+ will wait until they all finish then return the first result returned since the previous call to +value+:
+If there are multiple blocks in a job, +value+ will wait until they all finish then return the last value received:
job.value {|b| p b } # => 4294967296.0
-You can then repeatedly call value to retrieve any additional results. Note that the returned order is undefined, since asynchronous blocks can complete in any order.
+=== Job#values: Returning All Values
+Note that values may be received out of order, since they may take differing amounts of time to complete. If you need to force a particular ordering, create a new +Job+ or call +join+ before submitting the block.
+
+Additionally, you can call +values+ to obtain all the values:
+
+ @values = job.values
+ puts @values # => [1.0E50, 4294967296.0]
+
+Note that unlike +value+ this will not +wait+ or +join+, and thus does not have an asynchronous equivalent.
+
== Dispatch::Proxy: Protecting Shared Data
Concurrency would be easy if everything was {embarrassingly parallel}[http://en.wikipedia.org/wiki/Embarrassingly_parallel], but it becomes tricky when we need to share data between threads. If two threads try to modify the same object at the same time, it could lead to inconsistent (read: _corrupt_) data. There are well-known techniques for preventing this sort of data corruption (e.g. locks[http://en.wikipedia.org/wiki/Lock_(computer_science)] andmutexes[http://en.wikipedia.org/wiki/Mutual%20eclusion]), but these have their own well-known problems (e.g., deadlock[http://en.wikipedia.org/wiki/Deadlock], and {priority inversion}[http://en.wikipedia.org/wiki/Priority_inversion]).
@@ -97,8 +103,12 @@
then ask it to wrap the object you want to modify from multiple threads:
@hash = job.synchronize {}
- @hash.class # => Dispatch::Proxy
+ puts @hash.class # => Dispatch::Proxy
+This is actually the same type of object used to manage the list of +values+:
+
+ puts job.values.class # => Dispatch::Proxy
+
=== method_missing: Using Proxies
The Proxy object can be called just as it if were the delegate object:
@@ -159,17 +169,24 @@
Jobs are useful when you want to run a single item in the background or to run many different operations at once. But if you want to run the _same_ operation multiple times, you can take advantage of specialized GCD iterators. The Dispatch module defines "p_" variants of common Ruby iterators, making it trivial to parellelize existing operations.
-These may add significant overhead compared to the non-parallel version, though, so you should only use them when doing a lot of work. In addition, for simplicity they all are _synchronous_, meaning they won't return until all the work has completed (if you do need asynchrony, simply wrap them or their results in a +Job+).
+In addition, for simplicity they all are _synchronous_, meaning they won't return until all the work has completed.
=== Integer#p_times ===
-The first is defined on the +Integer+ class, supplementing +times+:
+The simplest iteration is defined on the +Integer+ class, supplementing +times+:
- 3.p_times { |i| puts 10**i } # => 1 10 100
+ 5.p_times { |i| puts 10**i } # => 1 10 100 1000 10000 10000
+Even this add noticeable overhead compared to the non-parallel version, so if you have a large number of relatively cheap iterations you can batch them together by specifying a +stride+:
+
+ 5.p_times(3) { |i| puts 10**i } # =>1 10 100 1000 10000 10000
+
+It doesn't change the result, but schedules fewer blocks thus amortizing the overhead over more work.
+
+
=== Enumerable ===
-The rest are all defined on +Enumerable+, so they are available from any class which mixes that in (e.g, +Array+, +Hash+, etc.).
+The rest are all defined on +Enumerable+, so they are available from any class which mixes that in (e.g, +Array+, +Hash+, etc.). These also can take an optional stride:
==== p_each ====
@@ -191,7 +208,7 @@
(0..2).p_mapreduce { |i| 10**i } # => 111
-This uses a parallel +inject+ (aka +reduce+) to return a single value by combining the results of +map+ via the ":+" message. You can also specify a different accumulator:
+This uses a parallel +inject+ (formerly known as +reduce+) to return a single value by combining the result of +map+ via the ":+" message. You can also pass a symbol representing a different accumulator operation (method or operator):
(0..2).p_mapreduce(:*) { |i| 10**i } # => 1000
Modified: MacRuby/trunk/lib/dispatch/job.rb
===================================================================
--- MacRuby/trunk/lib/dispatch/job.rb 2010-02-24 21:23:11 UTC (rev 3598)
+++ MacRuby/trunk/lib/dispatch/job.rb 2010-02-24 21:23:20 UTC (rev 3599)
@@ -4,12 +4,12 @@
# Duck-type +join+ and +value+ from +Thread+
class Job
# Create a Job that asynchronously dispatches the block
- attr_reader :group, :results
+ attr_reader :group, :values
def initialize(queue = Dispatch::Queue.concurrent, &block)
@queue = queue
@group = Group.new
- @results = synchronize([])
+ @values = synchronize([])
add(&block) if not block.nil?
end
@@ -19,7 +19,7 @@
# Submit block as part of the same dispatch group
def add(&block)
- @queue.async(@group) { @results << block.call }
+ @queue.async(@group) { @values << block.call }
end
# Wait until execution has completed.
@@ -45,7 +45,7 @@
# Remove and return the first value
def result
- @results.shift
+ @values[-1]
end
end
Modified: MacRuby/trunk/spec/macruby/library/dispatch/job_spec.rb
===================================================================
--- MacRuby/trunk/spec/macruby/library/dispatch/job_spec.rb 2010-02-24 21:23:11 UTC (rev 3598)
+++ MacRuby/trunk/spec/macruby/library/dispatch/job_spec.rb 2010-02-24 21:23:20 UTC (rev 3599)
@@ -32,13 +32,13 @@
end
end
- describe :results do
+ describe :values do
it "should return an instance of Dispatch::Proxy" do
- @job.results.should be_kind_of Dispatch::Proxy
+ @job.values.should be_kind_of Dispatch::Proxy
end
it "has a __value__ that is Enumerable" do
- @job.results.__value__.should be_kind_of Enumerable
+ @job.values.__value__.should be_kind_of Enumerable
end
end
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/macruby-changes/attachments/20100224/435794af/attachment.html>
More information about the macruby-changes
mailing list