0
# Thread Execution
1
2
Thread pool executor using Qt's QThread for CPU-intensive tasks. QThreadExecutor provides a concurrent.futures.Executor-compatible interface that integrates seamlessly with asyncio's run_in_executor functionality.
3
4
## Capabilities
5
6
### Thread Pool Management
7
8
Creates and manages a pool of QThread workers for executing blocking or CPU-intensive operations without blocking the main UI thread.
9
10
```python { .api }
11
class QThreadExecutor:
12
"""
13
ThreadExecutor that produces QThreads.
14
15
Same API as concurrent.futures.Executor with Qt integration.
16
17
Args:
18
max_workers: Maximum number of worker threads (default: 10)
19
stack_size: Stack size for each thread in bytes (auto-detected if None)
20
"""
21
def __init__(self, max_workers=10, stack_size=None): ...
22
```
23
24
#### Usage Example
25
26
```python
27
import asyncio
28
import time
29
from qasync import QEventLoop, QThreadExecutor
30
31
def cpu_intensive_task(n):
32
# Simulate CPU-intensive work
33
total = 0
34
for i in range(n * 1000000):
35
total += i
36
return total
37
38
async def main():
39
loop = asyncio.get_event_loop()
40
41
# Method 1: Use with event loop's run_in_executor
42
with QThreadExecutor(5) as executor:
43
result = await loop.run_in_executor(executor, cpu_intensive_task, 100)
44
print(f"Result: {result}")
45
46
# Method 2: Direct submission
47
executor = QThreadExecutor(3)
48
try:
49
future = executor.submit(cpu_intensive_task, 50)
50
result = future.result() # Blocks until complete
51
print(f"Direct result: {result}")
52
finally:
53
executor.shutdown()
54
```
55
56
### Task Submission
57
58
Submit callables to be executed in the thread pool, returning Future objects for result retrieval.
59
60
```python { .api }
61
def submit(self, callback, *args, **kwargs):
62
"""
63
Submit a callable to be executed in a worker thread.
64
65
Args:
66
callback: Callable to execute
67
*args: Positional arguments for callback
68
**kwargs: Keyword arguments for callback
69
70
Returns:
71
concurrent.futures.Future: Future representing the execution
72
73
Raises:
74
RuntimeError: If executor has been shutdown
75
"""
76
```
77
78
#### Usage Example
79
80
```python
81
from qasync import QThreadExecutor
82
import time
83
84
def blocking_operation(duration, message):
85
time.sleep(duration)
86
return f"Completed: {message}"
87
88
executor = QThreadExecutor(2)
89
90
# Submit multiple tasks
91
future1 = executor.submit(blocking_operation, 1, "Task 1")
92
future2 = executor.submit(blocking_operation, 2, "Task 2")
93
94
# Wait for results
95
print(future1.result()) # "Completed: Task 1"
96
print(future2.result()) # "Completed: Task 2"
97
98
executor.shutdown()
99
```
100
101
### Executor Lifecycle
102
103
Control the lifecycle of the thread pool executor, including graceful shutdown and resource cleanup.
104
105
```python { .api }
106
def shutdown(self, wait=True):
107
"""
108
Shutdown the executor and clean up worker threads.
109
110
Args:
111
wait: If True, wait for all pending tasks to complete
112
113
Raises:
114
RuntimeError: If executor has already been shutdown
115
"""
116
```
117
118
### Context Manager Support
119
120
Use QThreadExecutor as a context manager for automatic resource management.
121
122
```python { .api }
123
def __enter__(self):
124
"""
125
Context manager entry.
126
127
Returns:
128
QThreadExecutor: Self
129
130
Raises:
131
RuntimeError: If executor has been shutdown
132
"""
133
134
def __exit__(self, *args):
135
"""Context manager exit - shuts down executor."""
136
```
137
138
#### Usage Example
139
140
```python
141
from qasync import QThreadExecutor
142
143
def process_data(data):
144
# Process data (blocking operation)
145
return [x * 2 for x in data]
146
147
# Automatic cleanup with context manager
148
with QThreadExecutor(4) as executor:
149
futures = []
150
for i in range(5):
151
future = executor.submit(process_data, list(range(i * 10, (i + 1) * 10)))
152
futures.append(future)
153
154
# Collect results
155
results = [future.result() for future in futures]
156
print("All tasks completed:", len(results))
157
# Executor is automatically shutdown
158
```
159
160
### Asyncio Integration
161
162
Seamless integration with asyncio event loops through run_in_executor.
163
164
#### Usage Example
165
166
```python
167
import asyncio
168
from qasync import QEventLoop, QThreadExecutor
169
170
def blocking_computation(n):
171
result = sum(i * i for i in range(n))
172
return result
173
174
async def async_workflow():
175
loop = asyncio.get_event_loop()
176
177
# Run blocking operations concurrently
178
with QThreadExecutor(3) as executor:
179
tasks = [
180
loop.run_in_executor(executor, blocking_computation, 1000),
181
loop.run_in_executor(executor, blocking_computation, 2000),
182
loop.run_in_executor(executor, blocking_computation, 3000),
183
]
184
185
results = await asyncio.gather(*tasks)
186
print("Concurrent results:", results)
187
188
# Run with Qt event loop
189
import sys
190
from PySide6.QtWidgets import QApplication
191
192
app = QApplication(sys.argv)
193
asyncio.run(async_workflow(), loop_factory=QEventLoop)
194
```
195
196
## Thread Configuration
197
198
### Stack Size Management
199
200
The executor automatically configures appropriate stack sizes based on the platform:
201
202
- **macOS**: 16 MB stack size
203
- **FreeBSD**: 4 MB stack size
204
- **AIX**: 2 MB stack size
205
- **Other platforms**: Uses system default
206
207
Custom stack sizes can be specified during initialization:
208
209
```python
210
# Custom stack size (8 MB)
211
executor = QThreadExecutor(max_workers=5, stack_size=8 * 1024 * 1024)
212
```
213
214
### Worker Thread Management
215
216
Each QThreadExecutor manages a fixed pool of QThread workers that:
217
218
- Start immediately upon executor creation
219
- Process tasks from a shared queue
220
- Handle exceptions and propagate them through Future objects
221
- Clean up resources when shutting down
222
- Support graceful termination
223
224
## Error Handling
225
226
The executor properly handles and propagates exceptions from worker threads:
227
228
```python
229
from qasync import QThreadExecutor
230
231
def failing_task():
232
raise ValueError("Something went wrong!")
233
234
executor = QThreadExecutor(1)
235
future = executor.submit(failing_task)
236
237
try:
238
result = future.result()
239
except ValueError as e:
240
print(f"Task failed: {e}")
241
242
executor.shutdown()
243
```
244
245
## Limitations
246
247
```python { .api }
248
def map(self, func, *iterables, timeout=None):
249
"""
250
Not implemented - raises NotImplementedError.
251
Use asyncio.gather with run_in_executor instead.
252
"""
253
```
254
255
For mapping operations, use asyncio patterns instead:
256
257
```python
258
import asyncio
259
from qasync import QThreadExecutor
260
261
async def map_with_executor(func, iterable):
262
loop = asyncio.get_event_loop()
263
with QThreadExecutor() as executor:
264
tasks = [loop.run_in_executor(executor, func, item) for item in iterable]
265
return await asyncio.gather(*tasks)
266
```