|
20 | 20 | ##
|
21 | 21 | ## **Note:** Channels cannot be passed between threads. Use globals or pass
|
22 | 22 | ## them by `ptr`.
|
| 23 | +## |
| 24 | +## Example |
| 25 | +## ======= |
| 26 | +## The following is a simple example of two different ways to use channels: |
| 27 | +## blocking and non-blocking. |
| 28 | +## |
| 29 | +## .. code-block :: Nim |
| 30 | +## # Be sure to compile with --threads:on. |
| 31 | +## # The channels and threads modules are part of system and should not be |
| 32 | +## # imported. |
| 33 | +## import os |
| 34 | +## |
| 35 | +## # Channels can either be: |
| 36 | +## # - declared at the module level, or |
| 37 | +## # - passed to procedures by ptr (raw pointer) -- see note on safety. |
| 38 | +## # |
| 39 | +## # For simplicity, in this example a channel is declared at module scope. |
| 40 | +## # Channels are generic, and they include support for passing objects between |
| 41 | +## # threads. |
| 42 | +## # Note that objects passed through channels will be deeply copied. |
| 43 | +## var chan: Channel[string] |
| 44 | +## |
| 45 | +## # This proc will be run in another thread using the threads module. |
| 46 | +## proc firstWorker() = |
| 47 | +## chan.send("Hello World!") |
| 48 | +## |
| 49 | +## # This is another proc to run in a background thread. This proc takes a while |
| 50 | +## # to send the message since it sleeps for 2 seconds (or 2000 milliseconds). |
| 51 | +## proc secondWorker() = |
| 52 | +## sleep(2000) |
| 53 | +## chan.send("Another message") |
| 54 | +## |
| 55 | +## # Initialize the channel. |
| 56 | +## chan.open() |
| 57 | +## |
| 58 | +## # Launch the worker. |
| 59 | +## var worker1: Thread[void] |
| 60 | +## createThread(worker1, firstWorker) |
| 61 | +## |
| 62 | +## # Block until the message arrives, then print it out. |
| 63 | +## echo chan.recv() # "Hello World!" |
| 64 | +## |
| 65 | +## # Wait for the thread to exit before moving on to the next example. |
| 66 | +## worker1.joinThread() |
| 67 | +## |
| 68 | +## # Launch the other worker. |
| 69 | +## var worker2: Thread[void] |
| 70 | +## createThread(worker2, secondWorker) |
| 71 | +## # This time, use a non-blocking approach with tryRecv. |
| 72 | +## # Since the main thread is not blocked, it could be used to perform other |
| 73 | +## # useful work while it waits for data to arrive on the channel. |
| 74 | +## while true: |
| 75 | +## let tried = chan.tryRecv() |
| 76 | +## if tried.dataAvailable: |
| 77 | +## echo tried.msg # "Another message" |
| 78 | +## break |
| 79 | +## |
| 80 | +## echo "Pretend I'm doing useful work..." |
| 81 | +## # For this example, sleep in order not to flood stdout with the above |
| 82 | +## # message. |
| 83 | +## sleep(400) |
| 84 | +## |
| 85 | +## # Wait for the second thread to exit before cleaning up the channel. |
| 86 | +## worker2.joinThread() |
| 87 | +## |
| 88 | +## # Clean up the channel. |
| 89 | +## chan.close() |
| 90 | +## |
| 91 | +## Sample output |
| 92 | +## ------------- |
| 93 | +## The program should output something similar to this, but keep in mind that |
| 94 | +## exact results may vary in the real world:: |
| 95 | +## Hello World! |
| 96 | +## Pretend I'm doing useful work... |
| 97 | +## Pretend I'm doing useful work... |
| 98 | +## Pretend I'm doing useful work... |
| 99 | +## Pretend I'm doing useful work... |
| 100 | +## Pretend I'm doing useful work... |
| 101 | +## Another message |
| 102 | +## |
| 103 | +## Passing Channels Safely |
| 104 | +## ---------------------- |
| 105 | +## Note that when passing objects to procedures on another thread by pointer |
| 106 | +## (for example through a thread's argument), objects created using the default |
| 107 | +## allocator will use thread-local, GC-managed memory. Thus it is generally |
| 108 | +## safer to store channel objects in global variables (as in the above example), |
| 109 | +## in which case they will use a process-wide (thread-safe) shared heap. |
| 110 | +## |
| 111 | +## However, it is possible to manually allocate shared memory for channels |
| 112 | +## using e.g. ``system.allocShared0`` and pass these pointers through thread |
| 113 | +## arguments: |
| 114 | +## |
| 115 | +## .. code-block :: Nim |
| 116 | +## proc worker(channel: ptr Channel[string]) = |
| 117 | +## let greeting = channel[].recv() |
| 118 | +## echo greeting |
| 119 | +## |
| 120 | +## proc localChannelExample() = |
| 121 | +## # Use allocShared0 to allocate some shared-heap memory and zero it. |
| 122 | +## # The usual warnings about dealing with raw pointers apply. Exercise caution. |
| 123 | +## var channel = cast[ptr Channel[string]]( |
| 124 | +## allocShared0(sizeof(Channel[string])) |
| 125 | +## ) |
| 126 | +## channel[].open() |
| 127 | +## # Create a thread which will receive the channel as an argument. |
| 128 | +## var thread: Thread[ptr Channel[string]] |
| 129 | +## createThread(thread, worker, channel) |
| 130 | +## channel[].send("Hello from the main thread!") |
| 131 | +## # Clean up resources. |
| 132 | +## thread.joinThread() |
| 133 | +## channel[].close() |
| 134 | +## deallocShared(channel) |
| 135 | +## |
| 136 | +## localChannelExample() # "Hello from the main thread!" |
23 | 137 |
|
24 | 138 | when not declared(ThisIsSystem):
|
25 | 139 | {.error: "You must not import this module explicitly".}
|
|
0 commit comments