0

Lets tallk about the next code sample:

#include <iostream>           // std::cout
#include <thread>             // std::thread
#include <mutex>              // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable
#include <deque>

std::mutex mtx;
std::condition_variable cv;
bool ready = false;
std::deque<int> queue;



void Inserter()
{
    int count = 10;
    while (count >= 0)
    {
        std::unique_lock<std::mutex> lock(mtx);
        queue.push_back(count);
        --count;
        lock.unlock();
    }
}

void Consumer()
{
    int count = 10, got;

    while (count >= 0)
    {
        std::unique_lock<std::mutex> lock(mtx);
        if (!queue.empty())
        {
            got = queue.back();
            queue.pop_back();
            std::cout << "We got: " << got << std::endl;
            --count;
            lock.unlock();
        }
        else
        {
            lock.unlock();
        }
    }
}

int main()
{
    std::thread t1(Inserter);
    std::thread t2(Consumer);

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

When I run this program I get "abort" but I shouldnt. The only reason I see here to get abort is when I leaves locked lock without opened it - but there is no any reason for a lock to stay locked because after every loop I passes I open the lock.

What do you see the reason is?

cubuspl42
  • 7,833
  • 4
  • 41
  • 65
fgefg efgv
  • 55
  • 1
  • 4

2 Answers2

1

You don't call std::thread::join in the main program. When t2 goes out of scope, you call its destructor, and because you haven't joined the thread, it causes an abort.

Incidentally, you don't need to call lock.unlock(). The whole point of std::unique_lock is that it will do that automatically when the object is destructed. (And it would be much cleaner to declare got right where you get the value out of the queue.)

So your code should look like:

#include <iostream>           // std::cout
#include <thread>             // std::thread
#include <mutex>              // std::mutex, std::unique_lock
#include <deque>

std::mutex mtx;
std::deque<int> queue;

void Inserter()
{
    for (int count = 10; count >= 0; --count)
    {
        std::unique_lock<std::mutex> lock(mtx);
        queue.push_back(count);
    }
}

void Consumer()
{
    for (int count = 10; count >= 0; )
    {
        std::unique_lock<std::mutex> lock(mtx);
        if (!queue.empty())
        {
            const int got = queue.back();
            queue.pop_back();
            std::cout << "We got: " << got << std::endl;
            --count;
        }
    }
}

int main()
{
    std::thread t1(Inserter);
    std::thread t2(Consumer);

    t1.join();
    t2.join();

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

The requirement to call join is because a common idiom is to make Inserter be a class derived from std::thread, and have the Inserter constructor pass a suitable thread function to the std::thread constructor. If the class contained member variables which the thread method accessed, it would be bad if the thread method kept running after the object was destructed. The standard requires you to avoid this by either calling join (in which case the thread method has finished), or by calling detach (in which case it is your responsibility to make sure you don't access variables which have gone out of scope).

0

To resolve the issue you need to call std::thread::join on your both threads

int main(){

       std::thread t1(Inserter);      
       std::thread t2(Consumer);
       t1.join();
       t2.join();

    return 0;
}

Demo : here

Steephen
  • 14,645
  • 7
  • 40
  • 47