blueloveTH 10 месяцев назад
Родитель
Сommit
076627fb76
1 измененных файлов с 140 добавлено и 0 удалено
  1. 140 0
      docs/features/threading.md

+ 140 - 0
docs/features/threading.md

@@ -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
+```