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 +66 -56
- data/lib/workers/task_group.rb +13 -4
- data/lib/workers/version.rb +1 -1
- data/workers.gemspec +2 -2
- metadata +5 -4
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
|
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
|
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
|
226
|
+
:pool => Workers.pool # The workers pool used to execute timer callbacks.
|
217
227
|
)
|
218
228
|
|
219
229
|
task = Workers::Task.new(
|
data/lib/workers/task_group.rb
CHANGED
@@ -10,7 +10,8 @@ module Workers
|
|
10
10
|
@pool = options[:pool] || Workers.pool
|
11
11
|
@state = :initialized
|
12
12
|
@tasks = []
|
13
|
-
@
|
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
|
-
@
|
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(@
|
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
|
-
@
|
97
|
+
@internal_lock.synchronize do
|
89
98
|
@finished_count += 1
|
90
99
|
@conditional.signal if @finished_count >= @tasks.count
|
91
100
|
end
|
data/lib/workers/version.rb
CHANGED
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{
|
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.
|
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-
|
12
|
+
date: 2013-05-24 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
|
-
description:
|
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: []
|