workers 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -19,8 +19,71 @@ Or install it yourself as:
19
19
 
20
20
  $ gem install workers
21
21
 
22
+ ## Tasks
23
+
24
+ Tasks and task groups build on top of pools of worker threads.
25
+ They provide a means of parallelizing expensive computations, collecing results, and handling exceptions.
26
+ These are the classes you normally work with in your application level code because they remove boiletplate.
27
+
28
+ # Create a task group (it contains a pool of worker threads).
29
+ group = Workers::TaskGroup.new
30
+
31
+ # Add tasks to the group.
32
+ 10.times do |i|
33
+ 10.times do |j|
34
+ group.add(i, j) do
35
+ group.synchronize { puts "Computing #{i} * #{j}..." }
36
+ i * j # Last statement executed is the result of the task.
37
+ end
38
+ end
39
+ end
40
+
41
+ # Execute the tasks (blocks until the tasks complete).
42
+ # Returns true if all tasks succeed. False if any fail.
43
+ group.run
44
+
45
+ # Return an array of all the tasks.
46
+ group.tasks
47
+
48
+ # Return an array of the successful tasks.
49
+ group.successes
50
+
51
+ # Return an array of the failed tasks (raised an exception).
52
+ group.failures
53
+
54
+ # Review the results.
55
+ group.tasks.each do |t|
56
+ t.succeeded? # True or false (false if an exception occurred).
57
+ t.failed? # True or false (true if an exception occurred).
58
+ t.args # Input arguments (the value of i in this example).
59
+ t.result # Output value (the result of i * i in this example).
60
+ t.exception # The exception if one exists.
61
+ end
62
+
63
+ Note that instances of TaskGroup provide a 'synchronize' method.
64
+ This method uses a mutex so you can serialize portions of your tasks that aren't thread safe.
65
+
66
+ ## Parallel Map
67
+
68
+ Parallel Map is syntactic sugar for tasks and task groups:
69
+
70
+ group = Workers::TaskGroup.new
71
+ group.map([1,2,3,4,5]) { |i| i * i }
72
+
73
+ The above syntax is equivalent to the below:
74
+
75
+ Workers.map([1, 2, 3, 4, 5]) { |i| i * i }
76
+
77
+ Note that any task failures (exceptions) will cause an exception to be raised.
78
+ This means that parallel map is "all or nothing" where as tasks and task groups leave error processing up to you.
79
+
22
80
  ## Workers - Basic
23
81
 
82
+ There are many cases where tasks, task groups, and parallel map are too high-level.
83
+ Fortunately you can use the lower level Worker class to solve these problems.
84
+ The main purpose of the Worker class is to add an event system on top of the Thread class.
85
+ This greatly simplifies inter-thread communication.
86
+
24
87
  # Initialize a worker pool.
25
88
  pool = Workers::Pool.new
26
89
 
@@ -44,7 +107,7 @@ Or install it yourself as:
44
107
 
45
108
  The Worker class is designed to be customized through inheritence and its event system:
46
109
 
47
- # Create a custom worker class that handles custom commands.
110
+ # Create a subclass that handles custom events.
48
111
  class CustomWorker < Workers::Worker
49
112
  private
50
113
  def process_event(event)
@@ -75,7 +138,7 @@ The Worker class is designed to be customized through inheritence and its event
75
138
  Note that you can use custom workers without a pool.
76
139
  This effectively gives you direct access to a single event-driven thread.
77
140
 
78
- ## Pools - Advanced
141
+ ## Pools
79
142
 
80
143
  As shown above, pools effectively allow a group of workers to share a work queue.
81
144
  They have a few additional methods described below:
@@ -95,59 +158,6 @@ They have a few additional methods described below:
95
158
  # Resize the pool size to a specific value.
96
159
  pool.resize(20)
97
160
 
98
- ## Tasks
99
-
100
- Tasks and task groups build on top of worker pools.
101
- They provide a means of parallelizing expensive computations and collecing the results.
102
- These are the classes you normally work with in your application level code.
103
-
104
- # Create a task group (it contains a pool of workers).
105
- group = Workers::TaskGroup.new
106
-
107
- # Add tasks to the group.
108
- 10.times do |i|
109
- 10.times do |j|
110
- group.add(i, j) do
111
- i * j # Last statement executed is the result of the task.
112
- end
113
- end
114
- end
115
-
116
- # Execute the tasks (blocks until the tasks complete).
117
- # Returns true if all tasks succeed. False if any fail.
118
- group.run
119
-
120
- # Return an array of all the tasks.
121
- group.tasks
122
-
123
- # Return an array of the successful tasks.
124
- group.successes
125
-
126
- # Return an array of the failed tasks.
127
- group.failures
128
-
129
- # Review the results.
130
- group.tasks.each do |t|
131
- t.succeeded? # True or false (false if an exception occurred).
132
- t.failed? # True or false (true if an exception occurred).
133
- t.args # Input arguments (the value of i in this example).
134
- t.result # Output value (the result of i * i in this example).
135
- t.exception # The exception if one exists.
136
- end
137
-
138
- ## Parallel Map
139
-
140
- Task groups and tasks are the building blocks of parallel map:
141
-
142
- group = Workers::TaskGroup.new
143
- group.map([1,2,3,4,5]) { |i| i * i }
144
-
145
- The below syntax is equivalent to the above:
146
-
147
- Workers.map([1, 2, 3, 4, 5]) { |i| i * i }
148
-
149
- Note that any failures will cause an exception to be raised.
150
-
151
161
  ## Timers
152
162
 
153
163
  Timers provide a way to execute code in the future:
@@ -213,7 +223,7 @@ You can create additional or custom ones as necessary:
213
223
 
214
224
  group = Workers::TaskGroup.new(
215
225
  :logger => nil, # Ruby logger instance.
216
- :pool => Workers::Pool.new # The workers pool used to execute timer callbacks.
226
+ :pool => Workers.pool # The workers pool used to execute timer callbacks.
217
227
  )
218
228
 
219
229
  task = Workers::Task.new(
@@ -10,7 +10,8 @@ module Workers
10
10
  @pool = options[:pool] || Workers.pool
11
11
  @state = :initialized
12
12
  @tasks = []
13
- @lock = Mutex.new
13
+ @internal_lock = Mutex.new
14
+ @external_lock = Mutex.new
14
15
  @finished_count = 0
15
16
  @conditional = ConditionVariable.new
16
17
 
@@ -35,12 +36,12 @@ module Workers
35
36
  @state = :running
36
37
  @run_thread = Thread.current
37
38
 
38
- @lock.synchronize do
39
+ @internal_lock.synchronize do
39
40
  @tasks.each do |task|
40
41
  @pool.perform { task.run }
41
42
  end
42
43
 
43
- @conditional.wait(@lock)
44
+ @conditional.wait(@internal_lock)
44
45
  end
45
46
 
46
47
  return @tasks.all? { |t| t.succeeded? }
@@ -74,6 +75,14 @@ module Workers
74
75
  return tasks.map { |t| t.result }
75
76
  end
76
77
 
78
+ # Convenient mutex to be used by a users's task code that needs serializing.
79
+ # This should NEVER be used by TaskGroup code (use the @internal_lock instead);
80
+ def synchronize(&block)
81
+ @external_lock.synchronize { block.call }
82
+
83
+ return nil
84
+ end
85
+
77
86
  private
78
87
 
79
88
  def state!(*args)
@@ -85,7 +94,7 @@ module Workers
85
94
  end
86
95
 
87
96
  def finished(task)
88
- @lock.synchronize do
97
+ @internal_lock.synchronize do
89
98
  @finished_count += 1
90
99
  @conditional.signal if @finished_count >= @tasks.count
91
100
  end
@@ -1,3 +1,3 @@
1
1
  module Workers
2
- VERSION = '0.1.0'
2
+ VERSION = '0.1.1'
3
3
  end
data/workers.gemspec CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |gem|
8
8
  gem.version = Workers::VERSION
9
9
  gem.authors = ['Chad Remesch']
10
10
  gem.email = ['chad@remesch.com']
11
- gem.description = %q{Simple Ruby workers for performing work in background threads.}
12
- gem.summary = %q{Workers is a Ruby gem for performing work in background threads. Design goals include high performance, low latency, simple API, customizability, and multi-layered architecture.}
11
+ gem.description = %q{Workers is a Ruby gem for performing work in background threads.}
12
+ gem.summary = %q{Workers is a Ruby gem for performing work in background threads. Design goals include high performance, low latency, simple API, customizability, and multi-layered architecture. It provides a number of simple to use classes that solve a wide range of concurrency problems.}
13
13
  gem.homepage = 'https://github.com/chadrem/workers'
14
14
 
15
15
  gem.files = `git ls-files`.split($/)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: workers
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,9 +9,9 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-05-18 00:00:00.000000000 Z
12
+ date: 2013-05-24 00:00:00.000000000 Z
13
13
  dependencies: []
14
- description: Simple Ruby workers for performing work in background threads.
14
+ description: Workers is a Ruby gem for performing work in background threads.
15
15
  email:
16
16
  - chad@remesch.com
17
17
  executables: []
@@ -61,5 +61,6 @@ signing_key:
61
61
  specification_version: 3
62
62
  summary: Workers is a Ruby gem for performing work in background threads. Design goals
63
63
  include high performance, low latency, simple API, customizability, and multi-layered
64
- architecture.
64
+ architecture. It provides a number of simple to use classes that solve a wide range
65
+ of concurrency problems.
65
66
  test_files: []