Exercise 4: Working with the Concurrency Runtime Events

Figure 1

Now that you understand how the Concurrency Runtime takes advantage of UMS to schedule threads to yield execution to other threads when a thread blocks, let’s try and see this behavior in code.

In this exercise you will work to schedule various tasks using a Win32 event and observe its behavior when a thread blocks and other threads are waiting. You will then use the Concurrency Runtime event instead of the Win32 event to see if there are any chances when the threads are scheduled.

Part 1: Scheduling the Work

  1. Open the Exercise-4 solution file found at the following folder:

    C:\Server 2008 R2 Labs\Working with the CRT\Exercise-4\

  2. From the Solution Explorer, double click the Exercise-4.cpp file under the Sources folder.
  3. Examine the code for the WindowsEvent class.

    Note:
    This class is a wrapper class to a windows event. We’ve made the class have the same interface as our runtime cooperative event so that you can easily switch back and forth to appreciate the benefits of cooperative scheduling by later on just changing the event declaration.

  4. Scroll down to the main function (around line 37). Write the following line below the comment that reads:

    C++

    //Create a scheduler that uses two threads: CurrentScheduler::Create(SchedulerPolicy(1, MaxConcurrency, 2));
    Note:
    The Concurrency Runtime scheduler is policy driven, so we are talking to the current scheduler and creating a new scheduler policy that will allow a maximum of 2 threads, specified by the parameters sent to SchedulerPolicy.

  5. Below that comment that reads //Declare an Event, write the following line of code;

    C++

    WindowsEvent e;
    Note:
    This is the straightforward declaration of the WindowsEvent wrapper class that you previously examined.

  6. Below the comment that reads //Create a Lambda Waiting for the Event, write the following code snippet:

    auto task = [&e]() { printf_s("Waiting in a task for the event\n"); e.wait();
    Note:
    };

    The auto keyword in this case is analogous to the var type if you are familiar with VB.Net – it lets the compiler determine the type. In this case we are defining what the task will do when it is later scheduler to run. The lambda [&e] sets the event to be passed by reference to the body of the function that requires no parameters.

    The function will print the message to screen and will wait for the event e to be set before going any further.

  7. In order to schedule the tasks, we will use a taskgroup. To do so, type down the following code snippet below the comment that reads//Create a taskgroup and schedule multiple copies of the task

    C++

    task_group tg; for(int i = 0; i < 10; ++i) { tg.run(task);
    Note:
    }

    A task_group is a class defined in <ppl.h> and is used to schedule tasks, wait for them all to complete or cancel any outstanding tasks assigned to it. In this case we are adding 10 instances of the task we defined in step 6 to run.

  8. In order to allow the tasks to be scheduled and understand what’s going on underneath, sleep the main thread for one second by writing the following line below the comment //Sleep

    Sleep(1000);

  9. We will now set the event and all tasks that were currently waiting for this to happen will continue execution. Write down the following line below the comment//Wait for the events:

    C++

    printf_s(" Setting the event\n");
  10. e.set();
  11. Next, we must wait for all tasks to finish before going any further. The task_group class allows this by using the wait method. Write down the following line below the //Wait for the tasks comment:

    C++

    tg.wait();

  12. From the Debug menu, select Build Rebuild Solution(click Yes if prompted to build the project)
Note:
Your build should succeed. If you encounter any errors, please backtrack your steps and double-check each instruction.

Part 2: Running the Windows Event

Note:
Now that the code is ready, you will run the program and observe its behavior when the tasks are running. Keep in mind at all times that our custom scheduler policy only allows two concurrent threads to run.

  1. From the Debug menu, select Start without Debugging(click Yes if prompted to build the project)
  2. Take a look at the order in which the tasks arescheduled:

    Figure 2

    Note:
    By looking at the output, you can see that two tasks are immediately set. Since we are not using a cooperative scheduling, the two threads will not yield to other threads even though they are sitting idle waiting for the event to take place.

  3. Press any key to close the program and return to Visual Studio.

Part 3: Running the Cooperative Event

  1. To run the tasks using the cooperative scheduler, change the windows event to a Concurrency Runtime event by replacing the line below the //Declare an Event comment:

    C++

    WindowsEvent e;

    To:

    C++

    event e

  2. From the Debug menu, select Start without Debugging(click Yes if prompted to build the project)

    Note:
    Take a look at the order in which the tasks are scheduled:

    Figure 3

Note:
Since now the tasks are scheduled using the Concurrency Runtime event, they yield when they are waiting for a particular event instead of blocking any incoming thread to execute.