Create threading.md

This commit is contained in:
blueloveTH 2025-04-13 23:06:20 +08:00
parent cdbe55b539
commit 076627fb76

140
docs/features/threading.md Normal file
View File

@ -0,0 +1,140 @@
---
icon: dot
title: Threading
---
pocketpy organizes its state by `VM` structure.
Users can have at maximum 16 `VM` instances (index from 0 to 15).
Each `VM` instance can only be accessed by exactly one thread at a time.
If you are trying to run two python scripts in parallel refering the same `VM` instance,
you will crash it definitely.
However, there are two ways to achieve multi-threading in pocketpy.
One way is to use a native threading library such as `pthread`.
You can wrap the multi-threading logic into a C function and bind it to pocketpy.
Be careful and not to access the same `VM` instance from multiple threads at the same time.
You need to lock critical resources or perform a deep copy of all needed data.
## ComputeThread
The other way is to use `pkpy.ComputeThread`.
It is like an isolate in Dart language.
`ComputeThread` is a true multi-threading model to allow you run python scripts in parallel without lock,
backed by a separate `VM` instance.
`ComputeThread` is highly designed for computational intensive tasks in games.
For example, you can run game logic in main thread (VM 0) and run world generation in another thread (e.g. VM 1).
```mermaid
graph TD
subgraph Main Thread
A[Game Start]
B[Submit WorldGen Job]
C[Frame 1]
D[Frame 2]
E[Frame 3]
F[...]
G[Get WorldGen Result]
H[Render World]
end
subgraph WorldGen Thread
O[Generate Biomes]
P[Generate Terrain]
Q[Generate Creatures]
R[Dump Result]
end
A --> B
B --> C
C --> D
D --> E
E --> F
F --> G
G --> H
O --> P
P --> Q
Q --> R
B --> O
R --> G
```
#### `main.py`
```python
import time
from pkpy import ComputeThread
thread = ComputeThread(1)
print("Game Start")
# import worldgen.py
thread.exec_blocked('from worldgen import gen_world')
print("Submit WorldGen Job")
thread.call('gen_world', 3, (100, 100), 10)
# wait for worldgen to finish
for i in range(1, 100000):
print('Frame:', i)
time.sleep(1)
if thread.is_done:
break
error = thread.last_error()
if error is not None:
print("Error:", error)
else:
retval = thread.last_retval()
biomes = retval['biomes']
terrain = retval['terrain']
creatures = retval['creatures']
print("World Generation Complete", len(biomes), len(terrain), len(creatures))
```
#### `worldgen.py`
```python
import time
import random
def gen_world(biome_count: int, terrain_size: tuple[int, int], creature_count: int) -> dict:
# simulate a long computation
time.sleep(3)
# generate world data
all_biomes = ["forest", "desert", "ocean", "mountain", "swamp"]
all_creatures = ["wolf", "bear", "fish", "bird", "lizard"]
width, height = terrain_size
terrain_data = [
random.randint(1, 10)
for _ in range(width * height)
]
creatures = [
{
"name": random.choice(all_creatures),
"x": random.randint(0, width - 1),
"y": random.randint(0, height - 1),
}
for i in range(creature_count)
]
return {
"biomes": all_biomes[:biome_count],
"terrain": terrain_data,
"creatures": creatures,
}
```
Run `main.py` and you will see the result like this:
```
Game Start
Submit WorldGen Job
Frame: 1
Frame: 2
Frame: 3
Frame: 4
World Generation Complete 3 10000 10
```