fizx-thread_pool 0.2.1 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +4 -0
- data/README +20 -0
- data/lib/thread_pool.rb +21 -8
- data/test/test_thread_pool.rb +25 -0
- metadata +1 -1
data/CHANGELOG
CHANGED
data/README
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
This code is still pretty early, and not yet used in production. That said, I am interested in feedback. You can send me a message on GitHub's internal messaging system (http://github.com/inbox/new/fizx).
|
2
|
+
|
1
3
|
A simple executor-style ThreadPool for Ruby (with tests, yay!)
|
2
4
|
|
3
5
|
Usage:
|
@@ -6,4 +8,22 @@ Usage:
|
|
6
8
|
require "thread_pool"
|
7
9
|
pool = ThreadPool.new(threads = 10)
|
8
10
|
pool.execute { puts "I'm writing from a thread" }
|
11
|
+
pool.join
|
12
|
+
|
13
|
+
It's often useful to make sure that the properties of Ruby's blocks don't bite you. The following code will usually print three nils, because n keeps changing.
|
14
|
+
|
15
|
+
pool = ThreadPool.new(threads = 10)
|
16
|
+
numbers = [1, 2, 3]
|
17
|
+
while n = numbers.shift
|
18
|
+
pool.execute { puts n.inspect }
|
19
|
+
end
|
20
|
+
pool.join
|
21
|
+
|
22
|
+
Passing arguments to execute avoids this. i.e.:
|
23
|
+
|
24
|
+
pool = ThreadPool.new(threads = 10)
|
25
|
+
numbers = [1, 2, 3]
|
26
|
+
while n = numbers.shift
|
27
|
+
pool.execute(n) {|local| puts local.inspect }
|
28
|
+
end
|
9
29
|
pool.join
|
data/lib/thread_pool.rb
CHANGED
@@ -1,17 +1,30 @@
|
|
1
1
|
# Hooray
|
2
2
|
require "thread"
|
3
|
+
require "rubygems"
|
4
|
+
require "loggable"
|
5
|
+
|
3
6
|
class ThreadPool
|
7
|
+
include Loggable
|
4
8
|
class Executor
|
9
|
+
include Loggable
|
5
10
|
attr_reader :active
|
6
11
|
|
7
12
|
def initialize(queue, mutex)
|
8
13
|
@thread = Thread.new do
|
9
14
|
loop do
|
10
|
-
mutex.synchronize { @
|
11
|
-
if @
|
15
|
+
mutex.synchronize { @tuple = queue.shift }
|
16
|
+
if @tuple
|
17
|
+
debug "Executor: processing #{@tuple.hash}"
|
18
|
+
args, block = @tuple
|
12
19
|
@active = true
|
13
|
-
|
14
|
-
|
20
|
+
begin
|
21
|
+
block.call(*args)
|
22
|
+
rescue Exception => e
|
23
|
+
error e.message
|
24
|
+
error e.backtrace.join("\n")
|
25
|
+
end
|
26
|
+
block.complete = true
|
27
|
+
debug "Executor: complete #{@tuple.hash}"
|
15
28
|
else
|
16
29
|
@active = false
|
17
30
|
sleep 0.01
|
@@ -38,7 +51,7 @@ class ThreadPool
|
|
38
51
|
end
|
39
52
|
|
40
53
|
# Runs the block at some time in the near future
|
41
|
-
def execute(&block)
|
54
|
+
def execute(*args, &block)
|
42
55
|
init_completable(block)
|
43
56
|
|
44
57
|
if @queue_limit > 0
|
@@ -46,13 +59,13 @@ class ThreadPool
|
|
46
59
|
end
|
47
60
|
|
48
61
|
@mutex.synchronize do
|
49
|
-
@queue << block
|
62
|
+
@queue << [args, block]
|
50
63
|
end
|
51
64
|
end
|
52
65
|
|
53
66
|
# Runs the block at some time in the near future, and blocks until complete
|
54
|
-
def synchronous_execute(&block)
|
55
|
-
execute(&block)
|
67
|
+
def synchronous_execute(*args, &block)
|
68
|
+
execute(*args, &block)
|
56
69
|
sleep 0.01 until block.complete?
|
57
70
|
end
|
58
71
|
|
data/test/test_thread_pool.rb
CHANGED
@@ -30,6 +30,31 @@ class TestThreadPool < Test::Unit::TestCase
|
|
30
30
|
assert_equal n - THREADS, @pool.waiting
|
31
31
|
end
|
32
32
|
|
33
|
+
class A
|
34
|
+
def initialize(i)
|
35
|
+
@i = i
|
36
|
+
end
|
37
|
+
attr_reader :i
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_context
|
41
|
+
@foo = []
|
42
|
+
@bar = (0...5).to_a
|
43
|
+
while c = @bar.shift
|
44
|
+
@pool.execute { @foo << c }
|
45
|
+
end
|
46
|
+
@pool.join
|
47
|
+
assert_equal [nil] * 5, @foo
|
48
|
+
|
49
|
+
@foo = []
|
50
|
+
@bar = (0...5).to_a
|
51
|
+
while c = @bar.shift
|
52
|
+
@pool.execute(c) {|n| @foo << n }
|
53
|
+
end
|
54
|
+
@pool.join
|
55
|
+
assert_equal (0...5).to_a, @foo
|
56
|
+
end
|
57
|
+
|
33
58
|
def test_queue_limit
|
34
59
|
n = 50
|
35
60
|
@foo = 0
|