1

So I have two threads where they share the same variable, 'counter'. I want to synchronize my threads by only continuing execution once both threads have reached that point. Unfortunately I enter a deadlock state as my thread isn't changing it's checking variable. The way I have it is:

volatile int counter = 0;

    Thread() {

             - some calculations - 

            counter++;
            while(counter != 2) {
               std::this_thread::yield();
            }
            counter = 0;

            - rest of the calculations -
    }

The idea is that since I have 2 threads, once they reach that point - at different times - they will increment the counter. If the counter isn't equal to 2, then the thread that reached there first will have to wait until the other has incremented the counter so that they are synced up. Does anyone know where the issue lies here?

To add more information about the problem, I have two threads which perform half of the operations on an array. Once they are done, I want to make sure that they both have completed finish their calculations. Once they are, I can signal the printer thread to wake up and perform it's operation of printing and clearing the array. If I do this before both threads have completed, there will be issues.

Pseudo code:

Thread() {

    getLock()
    1/2 of the calculations on array
    releaseLock()

    wait for both to finish - this is the issue

    wake up printer thread

}
QQCompi
  • 225
  • 4
  • 14
  • Usually when we talk about thread *synchronization* we are talking about forcing the threads to run *in sequence* one after the other rather than *at the same time*. You seem to be trying to do the opposite. Can you explain why you want to get your threads running simultaneously? Incidentally its not possible to know for sure if your threads will ever run at the same time, that's down to the OS scheduler. – Galik Nov 08 '15 at 04:59
  • So essentially I am performing calculations on an array which are split up between the two threads. At the end, I want to print and clear this array using another thread. I need one thread to wake up the printing thread but I need both threads to have completed their executions on the array before I can wake up the printer thread. If they aren't synchronized, I will get an array that isn't completed. – QQCompi Nov 08 '15 at 05:01
  • volatile has no place in multithreading – Cubbi Dec 03 '15 at 19:28

3 Answers3

2

In situations like this, you must use an atomic counter.

std::atomic_uint counter = 0;

In the given example, there is also no sign that counter got initialized.

Andrew Lazarus
  • 18,205
  • 3
  • 35
  • 53
  • I have initialized it using `volatile int counter = 0;` but could you aid me in how to use an atomic counter? – QQCompi Nov 08 '15 at 04:57
  • atomics are the C++11 way of handling this, leaving `volatile` for hardware interfaces. http://en.cppreference.com/w/cpp/atomic Another problem is yield is only a hint. You might want `sleep`. I have a comment below that may also help. – Andrew Lazarus Nov 08 '15 at 05:12
1

You are probably looking for std::conditional_variable: A conditional variable allows one thread to signal to another thread. Because it doesn't look like you are using the counter, and you're only using it for synchronisation, here is some code from another answer (disclaimer: it's one of my answers) that shows std::conditional_variable processing logic on different threads, and performing synchronisation around a value:

unsigned int accountAmount;
std::mutex mx;
std::condition_variable cv;

void depositMoney()
{
    // go to the bank etc...
    // wait in line...
    {
        std::unique_lock<std::mutex> lock(mx);
        std::cout << "Depositing money" << std::endl;
        accountAmount += 5000;
    }
    // Notify others we're finished
    cv.notify_all();
}
void withdrawMoney()
{
    std::unique_lock<std::mutex> lock(mx);
    // Wait until we know the money is there
    cv.wait(lock);
    std::cout << "Withdrawing money" << std::endl;
    accountAmount -= 2000;
}
int main()
{
    accountAmount = 0;
    // Run both threads simultaneously:
    std::thread deposit(&depositMoney);
    std::thread withdraw(&withdrawMoney);
    // Wait for both threads to finish
    deposit.join();
    withdraw.join();
    std::cout << "All transactions processed. Final amount: " << accountAmount << std::endl;
    return 0;
}
Community
  • 1
  • 1
Tas
  • 7,023
  • 3
  • 36
  • 51
  • I believe this method is very similar to a cond_wait/cond_signal type implementation. I am not sure if I can use such an implementation due to the two threads having the same function, if add a wait call, both will begin to wait. – QQCompi Nov 08 '15 at 05:05
  • It is similar, but another approach would be to have two objects of this type, and a condition variable in their parent (in the logical sense), which `wait`s until the counter is correct. – Andrew Lazarus Nov 08 '15 at 05:13
1

I would look into using a countdown latch. The idea is to have one or more threads block until the desired operation is completed. In this case you want to wait until both threads are finished modifying the array.

Here is a simple example:

#include <condition_variable>
#include <mutex>
#include <thread>

class countdown_latch
{
public:
    countdown_latch(int count)
        : count_(count)
    {
    }

    void wait()
    {
        std::unique_lock<std::mutex> lock(mutex_);
        while (count_ > 0)
            condition_variable_.wait(lock);
    }

    void countdown()
    {
        std::lock_guard<std::mutex> lock(mutex_);
        --count_;
        if (count_ == 0)
            condition_variable_.notify_all();
    }

private:
    int count_;
    std::mutex mutex_;
    std::condition_variable condition_variable_;
};

and usage would look like this

std::atomic<int> result = 0;
countdown_latch latch(2);

void perform_work()
{
    ++result;
    latch.countdown();
}

int main() 
{
    std::thread t1(perform_work);
    std::thread t2(perform_work);

    latch.wait();

    std::cout << "result = " << result;

    t1.join();
    t2.join();
}
philm
  • 46
  • 3