or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

async-decorators.mdevent-loop.mdindex.mdthread-executor.mdutilities.md

thread-executor.mddocs/

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

```