diff --git a/examples/xtd.core.examples/threading/CMakeLists.txt b/examples/xtd.core.examples/threading/CMakeLists.txt index f1ba58a97099..a8406bec7585 100644 --- a/examples/xtd.core.examples/threading/CMakeLists.txt +++ b/examples/xtd.core.examples/threading/CMakeLists.txt @@ -5,6 +5,7 @@ find_package(xtd REQUIRED) add_projects( auto_reset_event + event_wait_handle interlocked interlocked_decrement mixing_std_and_xtd_threads diff --git a/examples/xtd.core.examples/threading/event_wait_handle/CMakeLists.txt b/examples/xtd.core.examples/threading/event_wait_handle/CMakeLists.txt new file mode 100644 index 000000000000..50e4ab046b27 --- /dev/null +++ b/examples/xtd.core.examples/threading/event_wait_handle/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.5) + +project(event_wait_handle) +find_package(xtd REQUIRED) +add_sources(README.md src/event_wait_handle.cpp) +target_type(CONSOLE_APPLICATION) diff --git a/examples/xtd.core.examples/threading/event_wait_handle/README.md b/examples/xtd.core.examples/threading/event_wait_handle/README.md new file mode 100644 index 000000000000..540b52770d66 --- /dev/null +++ b/examples/xtd.core.examples/threading/event_wait_handle/README.md @@ -0,0 +1,54 @@ +# auto_reset_event + +Shows how to use [xtd::threading::auto_reset_event](https://gammasoft71.github.io/xtd/reference_guides/latest/classxtd_1_1threading_1_1auto_reset_event.html) class. + +## Sources + +[src/auto_reset_event.cpp](src/auto_reset_event.cpp) + +[CMakeLists.txt](CMakeLists.txt) + +# Build and run + +Open "Command Prompt" or "Terminal". Navigate to the folder that contains the project and type the following: + +```cmake +xtdc run +``` + +# Output + +``` +Press Enter to create three threads and start them. +The threads wait on auto_reset_event #1, which was created +in the signaled state, so the first thread is released. +This puts auto_reset_event #1 into the unsignaled state. + +thread_0x16fe87000 waits on auto_reset_event #1. +thread_0x16fe87000 is released from auto_reset_event #1. +thread_0x16fe87000 waits on auto_reset_event #2. +thread_0x16ff13000 waits on auto_reset_event #1. +thread_0x16ff9f000 waits on auto_reset_event #1. +Press Enter to release another thread. + +thread_0x16ff9f000 is released from auto_reset_event #1. +thread_0x16ff9f000 waits on auto_reset_event #2. +Press Enter to release another thread. + +thread_0x16ff13000 is released from auto_reset_event #1. +thread_0x16ff13000 waits on auto_reset_event #2. + +All threads are now waiting on auto_reset_event #2. +Press Enter to release a thread. + +thread_0x16ff9f000 is released from auto_reset_event #2. +thread_0x16ff9f000 ends. +Press Enter to release a thread. + +thread_0x16fe87000 is released from auto_reset_event #2. +thread_0x16fe87000 ends. +Press Enter to release a thread. + +thread_0x16ff13000 is released from auto_reset_event #2. +thread_0x16ff13000 ends. +``` diff --git a/examples/xtd.core.examples/threading/event_wait_handle/src/event_wait_handle.cpp b/examples/xtd.core.examples/threading/event_wait_handle/src/event_wait_handle.cpp new file mode 100644 index 000000000000..09b72793bdf9 --- /dev/null +++ b/examples/xtd.core.examples/threading/event_wait_handle/src/event_wait_handle.cpp @@ -0,0 +1,154 @@ +#include +#include +#include +#include +#include + +using namespace xtd; +using namespace xtd::threading; + +class example { +private: + // The event_wait_handle used to demonstrate the difference + // between auto_reset and manual_reset synchronization events. + // + inline static event_wait_handle ewh; + + // A counter to make sure all threads are started and + // blocked before any are released. A Long is used to show + // the use of the 64-bit Interlocked methods. + // + inline static int64 thread_count = 0; + + // An auto_reset event that allows the main thread to block + // until an exiting thread has decremented the count. + // + inline static event_wait_handle clear_count {false, event_reset_mode::auto_reset}; + +public: + static void main() { + // Create an auto_reset event_wait_handle. + // + ewh = event_wait_handle {false, event_reset_mode::auto_reset}; + + // Create and start five numbered threads. Use the + // ParameterizedThreadStart delegate, so the thread + // number can be passed as an argument to the Start + // method. + for (int i = 0; i <= 4; i++) { + threads[i] = thread {parameterized_thread_start {thread_proc}}; + threads[i].start(i); + } + + // Wait until all the threads have started and blocked. + // When multiple threads use a 64-bit value on a 32-bit + // system, you must access the value through the + // Interlocked class to guarantee thread safety. + // + while (interlocked::read(thread_count) < 5) { + thread::sleep(500); + } + + // Release one thread each time the user presses ENTER, + // until all threads have been released. + // + while (interlocked::read(thread_count) > 0) { + console::write_line("Press ENTER to release a waiting thread."); + console::read_line(); + + // SignalAndWait signals the event_wait_handle, which + // releases exactly one thread before resetting, + // because it was created with auto_reset mode. + // SignalAndWait then blocks on clear_count, to + // allow the signaled thread to decrement the count + // before looping again. + // + wait_handle::signal_and_wait(ewh, clear_count); + } + console::write_line(); + + // Create a manual_reset event_wait_handle. + // + ewh = event_wait_handle(false, event_reset_mode::manual_reset); + + // Create and start five more numbered threads. + // + for(int i = 0; i <= 4; i++) { + threads[i] = thread {parameterized_thread_start {thread_proc}}; + threads[i].start(i); + } + + // Wait until all the threads have started and blocked. + // + while (interlocked::read(thread_count) < 5) { + thread::sleep(500); + } + + // Because the event_wait_handle was created with + // manual_reset mode, signaling it releases all the + // waiting threads. + // + console::write_line("Press ENTER to release the waiting threads."); + console::read_line(); + ewh.set(); + } + + static void thread_proc(std::any data) { + console::write_line("Thread {0} blocks.", as(data)); + // Increment the count of blocked threads. + interlocked::increment(thread_count); + + // Wait on the event_wait_handle. + ewh.wait_one(); + + console::write_line("Thread {0} exits.", as(data)); + // Decrement the count of blocked threads. + interlocked::decrement(thread_count); + + // After signaling ewh, the main thread blocks on + // clear_count until the signaled thread has + // decremented the count. Signal it now. + // + clear_count.set(); + } + + inline static std::vector threads = std::vector(4); +}; + +startup_(example); + +// This example produces output similar to the following: +// +// Thread 2 blocks. +// Thread 4 blocks. +// Thread 0 blocks. +// Thread 1 blocks. +// Thread 3 blocks. +// Press ENTER to release a waiting thread. +// +// Thread 4 exits. +// Press ENTER to release a waiting thread. +// +// Thread 2 exits. +// Press ENTER to release a waiting thread. +// +// Thread 0 exits. +// Press ENTER to release a waiting thread. +// +// Thread 1 exits. +// Press ENTER to release a waiting thread. +// +// Thread 3 exits. +// +// Thread 0 blocks. +// Thread 1 blocks. +// Thread 3 blocks. +// Thread 2 blocks. +// Thread 4 blocks. +// Press ENTER to release the waiting threads. +// +// Thread 2 exits. +// Thread 1 exits. +// Thread 4 exits. +// Thread 3 exits. +// Thread 0 exits.