You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: guides/best-practices/readme.md
+50-27Lines changed: 50 additions & 27 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -55,12 +55,24 @@ This expresses the intent to the caller that this method should only be invoked
55
55
56
56
## Use barriers to manage unbounded concurrency
57
57
58
-
Barriers provide a way to manage an unbounded number of tasks.
58
+
Barriers provide a way to manage an unbounded number of tasks. The top-level `Barrier` method creates a barrier with built-in load management using an `Async::Idler`.
59
59
60
60
```ruby
61
-
Asyncdo
62
-
barrier =Async::Barrier.new
63
-
61
+
Barrierdo |barrier|
62
+
items.each do |item|
63
+
barrier.async do
64
+
process(item)
65
+
end
66
+
end
67
+
end
68
+
```
69
+
70
+
The barrier will automatically wait for all tasks to complete and stop any outstanding tasks when the block exits. By default, it uses an `Async::Idler` to prevent system overload by scheduling tasks when the system load is below 80%.
71
+
72
+
If you want to process tasks in order of completion, you can explicitly call `wait` with a block:
73
+
74
+
```ruby
75
+
Barrierdo |barrier|
64
76
items.each do |item|
65
77
barrier.async do
66
78
process(item)
@@ -75,46 +87,57 @@ Async do
75
87
# If you don't want to wait for any more tasks you can break:
76
88
break
77
89
end
78
-
79
-
# Or just wait for all tasks to finish:
80
-
barrier.wait # May raise an exception if a task failed.
81
-
ensure
82
-
# Stop all outstanding tasks in the barrier:
83
-
barrier&.stop
90
+
end
91
+
```
92
+
93
+
To disable load management (not recommended for unbounded concurrency), you can pass `parent: nil`:
94
+
95
+
```ruby
96
+
Barrier(parent:nil) do |barrier|
97
+
# No load management - creates tasks as fast as possible
98
+
items.each do |item|
99
+
barrier.async do
100
+
process(item)
101
+
end
102
+
end
84
103
end
85
104
```
86
105
87
106
## Use a semaphore to limit the number of concurrent tasks
88
107
89
-
Semaphores allow you to limit the level of concurrency to a fixed number of tasks:
108
+
Semaphores allow you to limit the level of concurrency to a fixed number of tasks. When using semaphores with barriers, the barrier should be the root of your task hierarchy, and the semaphore should be a child of the barrier:
# Since the semaphore.async may block, we need to run the work scheduling in a child task:
97
-
task.async do
98
-
items.each do |item|
99
-
semaphore.async do
100
-
process(item)
101
-
end
114
+
items.each do |item|
115
+
semaphore.async do
116
+
process(item)
102
117
end
103
118
end
104
-
105
-
# Wait for all the work to complete:
106
-
barrier.wait
107
-
ensure
108
-
# Stop all outstanding tasks in the barrier:
109
-
barrier&.stop
110
119
end
111
120
```
112
121
113
-
In general, the barrier should be the root of your task hierarchy, and the semaphore should be a child of the barrier. This allows you to manage the lifetime of all tasks created by the semaphore, and ensures that all tasks are stopped when the barrier is stopped.
122
+
In this example, we use `parent: nil` for the barrier to disable load management, since the semaphore already provides concurrency control. The semaphore limits execution to 4 concurrent tasks, and the barrier ensures all tasks are stopped when the block exits.
114
123
115
124
### Idler
116
125
117
-
Idlers are like semaphores but with a limit defined by current processor utilization. In other words, an idler will do work up to a specific ratio of idle/busy time in the scheduler, and try to maintain that.
126
+
Idlers are like semaphores but with a limit defined by current processor utilization. In other words, an idler will schedule work up to a specific ratio of idle/busy time in the scheduler.
127
+
128
+
The top-level `Barrier` method uses an idler by default, making it safe for unbounded concurrency:
129
+
130
+
```ruby
131
+
Barrierdo |barrier| # Uses Async::Idler.new(0.8) by default
132
+
work.each do |work|
133
+
barrier.async do
134
+
work.call
135
+
end
136
+
end
137
+
end
138
+
```
139
+
140
+
You can also use an idler directly without a barrier:
0 commit comments