Multithreading in C++11 - part 2

In previous post we saw briefly how to create threads with various constructors. In this post I want to continue along the same lines. In last post we saw how to spawn the threads with simple execution code like functions, functors and labmda functions. In this post we will briefly explore invoking member-functions on objects as execution units of code. Lets start with the simple code to invoke non-virtual member function:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
#include <thread>

class Object
{
private:
    double d;
public:
    Object(double val = 10) : d(val)
    {}

    void Invoke() const
    {
        std::cout << "From thread 1: val of d is - " << this->d << std::endl;
    }

};

int main()
{
    Object obj(25);

    //  here we specify the function to be invoked on the object
    //
    std::thread t(&Object::Invoke, &obj);

    t.join();

    std::cin.ignore();
    return 0;
}

It seemed nice to be able to invoke member functions on objects. But lets try to be a bit more adventurous and see how to invoke virtual member functions:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
#include <thread>

class Base
{
public:
    virtual void Invoke() const
    {
        std::cout << "From thread 1 -- Base::Invoke" << std::endl;
    }
};

class Derived : public Base
{
public:
    virtual void Invoke() const
    {
        std::cout << "From thread 1 -- Derived::Invoke" << std::endl;
    }
};

int main()
{
    Derived obj;

    std::thread t(&Base::Invoke, &obj);

    t.join();

    std::cin.ignore();
    return 0;
}

Here is one more version to compute the dot product of two vectors, but this time by invoking the member function and using a shared variable. Each thread computes the dot product for the assigned segment and updates the shared dot-product variable atomically.

Here is the code:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
#include <iostream>
#include <thread>
#include <atomic>

class DotProduct
{
private:

    //  the arrays for which dot product is to
    //  be calculated
    //
    double* a;
    double* b;

    //  number of elements in array
    size_t numElems;

    //  assignment to this variables is atomic
    //  this is very nice way to ensure data consistency,
    //  when data shared between threads is updated
    //  concurrently
    //
    std::atomic<double> sum;

public:
    DotProduct(double* aa, double* bb, size_t numElems) :
      a(aa), b(bb), numElems(numElems), sum(0)
      {}

      double GetValue() const
      {
          return sum;
      }

      void Evaluate(int segmentId)
      {
          //    based on segment we will pick the range in array
          //    to operate on
          //

          //    I mean we assume there are 4 threads
          //
          size_t increment = numElems/4;
          size_t startIdx = segmentId * increment;
          size_t endIdx = (segmentId + 1) * increment;

          //    for now we can have local variable for accumulating
          //    the dot-product and then we will add it to the global
          //    sum, this ensures we are trying to avoid accessing
          //    shared memory to minimum, which leads to better
          //    performance
          //
          double dpSum = 0.0;
          for( size_t idx = startIdx; idx < endIdx; ++idx )
          {
              dpSum += a[idx] * b[idx];
          }

          //    once we are done just add the value to shared sum
          //
          sum =  sum + dpSum;
      }
};

int main()
{
    static const int NUMELEMENTS = 10000;
    double* a = new double[NUMELEMENTS];
    double* b = new double[NUMELEMENTS];

    for( size_t idx = 0; idx < NUMELEMENTS; ++idx )
    {
        a[idx] = idx;
        b[idx] = NUMELEMENTS - idx;
    }

    DotProduct dotProd(a,b,NUMELEMENTS);

    //  invoke Evaluate method on dotProd object
    //  and also pass 0 as the segmentId to the method
    //  similarly for others
    //
    std::thread t0(&DotProduct::Evaluate, &dotProd, 0);
    std::thread t1(&DotProduct::Evaluate, &dotProd, 1);
    std::thread t2(&DotProduct::Evaluate, &dotProd, 2);
    std::thread t3(&DotProduct::Evaluate, &dotProd, 3);

    //  now wait for threads to finish the work
    //
    t0.join();
    t1.join();
    t2.join();
    t3.join();

    //  we are done here, and it is safe to take the value
    //  from dotProd object
    //
    std::cout << "Dot product is: " << dotProd.GetValue() << std::endl;

    delete[] a;
    delete[] b;

    std::cin.ignore();
    return 0;
}

Again I ran into ugly assertions about mutex unlock at times, as in previous post

Update: This problem has been resolved in the latest version of Visual Studio (VS 2011 Beta at this point of time)