Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

C/C++

C++/CLI Threading: Part II


Atomicity and Interlocked Operations

Consider the following scenario: An application has multiple threads executing in parallel, with each thread having write access to some shared integer variable. Each thread simply increments that variable by 1, using ++value. That looks harmless enough; after all, this looks like an atomic operation, and on many systems, it is — at least from the point of view of a machine instruction. However, C++/CLI's execution environment does not universally guarantee this for all integer types.

To demonstrate this, the program in Listing Four has three threads, each concurrently incrementing a shared 64-bit integer variable 10 million times. It then displays that variable's final value, which, in theory, should be 30 million. The resulting application can be run in one of two modes: the default mode is unsynchronized and uses the ++ operator; the alternate mode, indicated by using a command-line argument of Y or y, uses a synchronized library increment function instead.

Listing Four

using namespace System;
using namespace System::Threading;

static bool interlocked = false;
const int maxCount = 10000000;
/*1*/   static long long value = 0;

void TMain()
{
    if (interlocked)
    {
        for (int i = 1; i <= maxCount; ++i)
        {
/*2*/           Interlocked::Increment(value);
        }
    }
    else
    {
        for (int i = 1; i <= maxCount; ++i)
        {
/*3*/           ++value;
        }
    }
}

int main(array<String^>^ argv)
{
    if (argv->Length == 1)
    {
        if (argv[0]->Equals("Y") || argv[0]->Equals("y"))
        {
            interlocked = true;
        }
    }

/*4*/   Thread^ t1 = gcnew Thread(gcnew ThreadStart(&TMain));
    Thread^ t2 = gcnew Thread(gcnew ThreadStart(&TMain));
    Thread^ t3 = gcnew Thread(gcnew ThreadStart(&TMain));

    t1->Start();
    t2->Start();
    t3->Start();
    t1->Join();
    t2->Join();
    t3->Join();

    Console::WriteLine("After {0} operations, value = {1}", 3 * maxCount, value);
}

When the standard ++ operator is used, five consecutive executions of the application resulted in the output shown in Figure 3. As we can see, the reported total falls far short of the correct answer. Simply stated, between 17 and 50 percent of the increments went unreported. When the same program was run in synchronized mode — that is, using Interlocked's Increment instead, all 30 million increments are done and reported correctly.

Figure 3: Output of Listing Four.

Output using the ++ operator

After 30000000 operations, value = 14323443
After 30000000 operations, value = 24521969
After 30000000 operations, value = 20000000
After 30000000 operations, value = 24245882
After 30000000 operations, value = 25404963


Output using Interlocked's Increment

After 30000000 operations, value = 30000000

Class Interlocked also has a Decrement function.

Exercises

To reinforce the material we've covered, perform the following activities:

  1. In Listing Four, change the type of the shared variable, value, and run the application with and without synchronization.
  2. In your implementation's documentation, carefully read the description of the Increment and Decrement functions in class Interlocked. Note that there are two sets, one for int and one for long long. Note also that there is no support for arguments of type unsigned int or unsigned long long.
  3. Carefully read the description of the other functions in Interlocked, especially Add, Exchange, and CompareExchange.
  4. The class Queue in Listing Five (available online, see link at the beginning of this article) implements a queue of strings. Modify this class so that it is thread safe; that is, provide support for multiple threads adding and/or removing nodes from the same queue at the same time. The class has two public functions:
  5.      void AddNode(String^ s);
         String^ RemoveNode();
    
    

  6. RemoveNode must not return to its caller until it has something to return; that is, it must wait indefinitely, if necessary, for a node to be added.
  7. In some other assembly, write a Main that creates three threads that each adds some fixed number of nodes, and one thread that removes nodes, all running asynchronously. Once all the adder threads have finished, have the main thread add one last string with the value "END" and wait for the remover thread to shut itself down, which it does when it sees this node. Hint: You will need to use the Wait, Pulse, and Join functions, and you might find it useful to use Sleep as well, to stagger the adder threads' actions.

Rex Jaeschke is an independent consultant, author, and seminar leader. He serves as editor of the Standards for C++/CLI, CLI, and C#. Rex can be reached at http://www.RexJaeschke.com/.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.