workers 0.1.4 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +69 -56
- data/lib/workers/task.rb +15 -6
- data/lib/workers/task_group.rb +7 -8
- data/lib/workers/version.rb +1 -1
- data/lib/workers.rb +3 -3
- metadata +1 -1
data/README.md
CHANGED
@@ -19,11 +19,29 @@ Or install it yourself as:
|
|
19
19
|
|
20
20
|
$ gem install workers
|
21
21
|
|
22
|
+
## Parallel Map
|
23
|
+
|
24
|
+
Parallel map is the simplest way to get started with the Workers gem.
|
25
|
+
It is similar to Ruby's built in Array#map method except each element is mapped in parallel.
|
26
|
+
|
27
|
+
Workers.map([1, 2, 3, 4, 5]) { |i| i * i }
|
28
|
+
|
29
|
+
Any exceptions while mapping with cause the entire map method to fail.
|
30
|
+
If your block is prone to temporary failures (exceptions), you can retry it.
|
31
|
+
|
32
|
+
Workers.map([1, 2, 3, 4, 5], :max_tries => 100) do |i|
|
33
|
+
if rand <= 0.5
|
34
|
+
puts "Sometimes I like to fail while computing #{i} * #{i}."
|
35
|
+
raise 'sad face'
|
36
|
+
end
|
37
|
+
|
38
|
+
i * i
|
39
|
+
end
|
40
|
+
|
22
41
|
## Tasks
|
23
42
|
|
24
|
-
Tasks and task groups
|
25
|
-
|
26
|
-
These are the classes you normally work with in your application level code because they remove boiletplate.
|
43
|
+
Tasks and task groups provide more flexibility than parallel map.
|
44
|
+
The main benefit is that you get to decide how you want to handle exceptions.
|
27
45
|
|
28
46
|
# Create a task group (it contains a pool of worker threads).
|
29
47
|
group = Workers::TaskGroup.new
|
@@ -31,7 +49,7 @@ These are the classes you normally work with in your application level code beca
|
|
31
49
|
# Add tasks to the group.
|
32
50
|
10.times do |i|
|
33
51
|
10.times do |j|
|
34
|
-
group.add
|
52
|
+
group.add do
|
35
53
|
group.synchronize { puts "Computing #{i} * #{j}..." }
|
36
54
|
i * j # Last statement executed is the result of the task.
|
37
55
|
end
|
@@ -63,25 +81,26 @@ These are the classes you normally work with in your application level code beca
|
|
63
81
|
Note that instances of TaskGroup provide a 'synchronize' method.
|
64
82
|
This method uses a mutex so you can serialize portions of your tasks that aren't thread safe.
|
65
83
|
|
66
|
-
|
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 }
|
84
|
+
#### Options
|
72
85
|
|
73
|
-
|
86
|
+
group = Workers::TaskGroup.new(
|
87
|
+
:logger => nil, # Ruby logger instance.
|
88
|
+
:pool => Workers.pool # The workers pool used to execute timer callbacks.
|
89
|
+
)
|
74
90
|
|
75
|
-
Workers.
|
91
|
+
task = Workers::Task.new(
|
92
|
+
:logger => nil, # Ruby logger instance.
|
93
|
+
:perform => proc {}, # Required option. Block of code to run.
|
94
|
+
:args => [], # Array of arguments passed to the 'perform' block.
|
95
|
+
:finished => nil, # Callback to execute after attempting to run the task.
|
96
|
+
:max_tries => 1, # Number of times to try completing the task (without an exception).
|
97
|
+
)
|
76
98
|
|
77
|
-
|
78
|
-
This means that parallel map is "all or nothing" where as tasks and task groups leave error processing up to you.
|
99
|
+
## Workers
|
79
100
|
|
80
|
-
|
101
|
+
#### Basic
|
81
102
|
|
82
|
-
|
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.
|
103
|
+
The main purpose of the Worker class is to add an event system on top of Ruby's built-in Thread class.
|
85
104
|
This greatly simplifies inter-thread communication.
|
86
105
|
|
87
106
|
# Initialize a worker pool.
|
@@ -103,7 +122,7 @@ This greatly simplifies inter-thread communication.
|
|
103
122
|
# Wait for the workers to shutdown.
|
104
123
|
pool.join
|
105
124
|
|
106
|
-
|
125
|
+
#### Advanced
|
107
126
|
|
108
127
|
The Worker class is designed to be customized through inheritence and its event system:
|
109
128
|
|
@@ -138,6 +157,13 @@ The Worker class is designed to be customized through inheritence and its event
|
|
138
157
|
Note that you can use custom workers without a pool.
|
139
158
|
This effectively gives you direct access to a single event-driven thread.
|
140
159
|
|
160
|
+
#### Options
|
161
|
+
|
162
|
+
worker = Workers::Worker.new(
|
163
|
+
:logger => nil, # Ruby Logger instance.
|
164
|
+
:input_queue => nil # Ruby Queue used for input events.
|
165
|
+
)
|
166
|
+
|
141
167
|
## Pools
|
142
168
|
|
143
169
|
As shown above, pools effectively allow a group of workers to share a work queue.
|
@@ -158,6 +184,14 @@ They have a few additional methods described below:
|
|
158
184
|
# Resize the pool size to a specific value.
|
159
185
|
pool.resize(20)
|
160
186
|
|
187
|
+
#### Options
|
188
|
+
|
189
|
+
pool = Workers::Pool.new(
|
190
|
+
:size => 20, # Number of threads to create.
|
191
|
+
:logger => nil, # Ruby Logger instance.
|
192
|
+
:worker_class => Workers::Worker # Class of worker to use for this pool.
|
193
|
+
)
|
194
|
+
|
161
195
|
## Timers
|
162
196
|
|
163
197
|
Timers provide a way to execute code in the future:
|
@@ -178,6 +212,21 @@ Timers provide a way to execute code in the future:
|
|
178
212
|
# Shutdown the timer.
|
179
213
|
timer.cancel
|
180
214
|
|
215
|
+
#### Options
|
216
|
+
|
217
|
+
timer = Workers::Timer.new(1,
|
218
|
+
:logger => nil, # Ruby logger instance.
|
219
|
+
:repeat => false, # Repeat the timer until 'cancel' is called.
|
220
|
+
:scheduler => Workers.scheduler, # The scheduler that manages execution.
|
221
|
+
:callback => nil # The proc to execute (provide this or a block, but not both).
|
222
|
+
)
|
223
|
+
|
224
|
+
timer = Workers::PeriodicTimer.new(1,
|
225
|
+
:logger => nil, # Ruby logger instance.
|
226
|
+
:scheduler => Workers.scheduler, # The scheduler that manages execution.
|
227
|
+
:callback => nil # The proc to execute (provide this or a block, but not both).
|
228
|
+
)
|
229
|
+
|
181
230
|
## Schedulers
|
182
231
|
|
183
232
|
Schedulers are what trigger Timers to fire.
|
@@ -201,43 +250,7 @@ You can create additional or custom ones as necessary:
|
|
201
250
|
# Shutdown the scheduler.
|
202
251
|
scheduler.dispose
|
203
252
|
|
204
|
-
|
205
|
-
|
206
|
-
pool = Workers::Pool.new(
|
207
|
-
:size => 20, # Number of threads to create.
|
208
|
-
:logger => nil, # Ruby Logger instance.
|
209
|
-
:worker_class => Workers::Worker # Class of worker to use for this pool.
|
210
|
-
)
|
211
|
-
|
212
|
-
worker = Workers::Worker.new(
|
213
|
-
:logger => nil, # Ruby Logger instance.
|
214
|
-
:input_queue => nil # Ruby Queue used for input events.
|
215
|
-
)
|
216
|
-
|
217
|
-
timer = Workers::Timer.new(1,
|
218
|
-
:logger => nil, # Ruby logger instance.
|
219
|
-
:repeat => false, # Repeat the timer until 'cancel' is called.
|
220
|
-
:scheduler => Workers.scheduler, # The scheduler that manages execution.
|
221
|
-
:callback => nil # The proc to execute (provide this or a block, but not both).
|
222
|
-
)
|
223
|
-
|
224
|
-
group = Workers::TaskGroup.new(
|
225
|
-
:logger => nil, # Ruby logger instance.
|
226
|
-
:pool => Workers.pool # The workers pool used to execute timer callbacks.
|
227
|
-
)
|
228
|
-
|
229
|
-
task = Workers::Task.new(
|
230
|
-
:logger => nil, # Ruby logger instance.
|
231
|
-
:perform => proc {}, # Required option. Block of code to run.
|
232
|
-
:args => [], # Array of arguments passed to the 'perform' block.
|
233
|
-
:finished => nil, # Callback to execute after attempting to run the task.
|
234
|
-
)
|
235
|
-
|
236
|
-
timer = Workers::PeriodicTimer.new(1,
|
237
|
-
:logger => nil, # Ruby logger instance.
|
238
|
-
:scheduler => Workers.scheduler, # The scheduler that manages execution.
|
239
|
-
:callback => nil # The proc to execute (provide this or a block, but not both).
|
240
|
-
)
|
253
|
+
#### Options
|
241
254
|
|
242
255
|
scheduler = Workers::Scheduler.new(
|
243
256
|
:logger => nil, # Ruby logger instance.
|
data/lib/workers/task.rb
CHANGED
@@ -12,7 +12,11 @@ module Workers
|
|
12
12
|
@args = options[:args] || []
|
13
13
|
@perform = options[:perform] || raise('Perform callback is required.')
|
14
14
|
@finished = options[:finished]
|
15
|
+
@max_tries = options[:max_tries] || 1
|
15
16
|
@state = :initialized
|
17
|
+
@tries = 0
|
18
|
+
|
19
|
+
raise 'max_tries must be >= 1' unless @max_tries >= 1
|
16
20
|
|
17
21
|
return nil
|
18
22
|
end
|
@@ -22,12 +26,17 @@ module Workers
|
|
22
26
|
|
23
27
|
@state = :running
|
24
28
|
|
25
|
-
|
26
|
-
@
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
29
|
+
while(@tries < @max_tries && @state != :succeeded)
|
30
|
+
@tries += 1
|
31
|
+
|
32
|
+
begin
|
33
|
+
@result = @perform.call(*@args)
|
34
|
+
@state = :succeeded
|
35
|
+
@exception = nil
|
36
|
+
rescue Exception => e
|
37
|
+
@state = :failed
|
38
|
+
@exception = e
|
39
|
+
end
|
31
40
|
end
|
32
41
|
|
33
42
|
@finished.call(self)
|
data/lib/workers/task_group.rb
CHANGED
@@ -18,14 +18,13 @@ module Workers
|
|
18
18
|
return nil
|
19
19
|
end
|
20
20
|
|
21
|
-
def add(
|
21
|
+
def add(options = {}, &block)
|
22
22
|
state!(:initialized)
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
end
|
24
|
+
options[:finished] = method(:finished)
|
25
|
+
options[:perform] ||= block
|
26
|
+
|
27
|
+
@tasks << Workers::Task.new(options)
|
29
28
|
|
30
29
|
return nil
|
31
30
|
end
|
@@ -57,9 +56,9 @@ module Workers
|
|
57
56
|
return @tasks.select { |t| t.failed? }
|
58
57
|
end
|
59
58
|
|
60
|
-
def map(inputs, &block)
|
59
|
+
def map(inputs, options = {}, &block)
|
61
60
|
inputs.each do |input|
|
62
|
-
add(input) do |i|
|
61
|
+
add(:args => input, :max_tries => options[:max_tries]) do |i|
|
63
62
|
block.call(i)
|
64
63
|
end
|
65
64
|
end
|
data/lib/workers/version.rb
CHANGED
data/lib/workers.rb
CHANGED
@@ -33,9 +33,9 @@ module Workers
|
|
33
33
|
@scheduler = val
|
34
34
|
end
|
35
35
|
|
36
|
-
def self.map(
|
37
|
-
return Workers::TaskGroup.new.map(
|
38
|
-
block.call(
|
36
|
+
def self.map(inputs, options = {}, &block)
|
37
|
+
return Workers::TaskGroup.new.map(inputs, options) do |i|
|
38
|
+
block.call(i)
|
39
39
|
end
|
40
40
|
end
|
41
41
|
end
|