Это тупик? Как избежать этого в многопоточности? - PullRequest
0 голосов
/ 03 октября 2019

Я изучаю параллелизм с C ++ 11 в настоящее время. Я написал кусок кода для курсовой работы. Однако код не завершает работу.

Моя среда:

Система: Windows 10

Компилятор: mingw-w64x86_64-8.1.0-posix-seh-rt_v6-rev0

Обновление:

Я думал, что "соединение" может закончить поток. Спасибо тем приятным парням, которые прокомментировали вопрос, я понял, что "присоединиться" не "убьет" ветку. Эта функция возвращает, когда выполнение потока завершено. Кроме того, проблема в моем коде, возможно, связана с тупиком. Я попробовал несколько способов решить эту проблему. Однако это также не работает.

Что я пробовал: добавление

if(ConsumerPtr->isConsumerDead()){
            break;
        }

в функцию void Producer::prods();. Это не работаетПозже я редактирую еще немного. Я добавил новую глобальную переменную, чтобы проверить, перестал ли потребитель работать. Кроме того, я добавил еще оператор if для проверки потребителя в районе void Buffer::put();. После этого я обнаружил, что код редко работает, но в большинстве случаев не работает. Я попытался отредактировать код std::this_thread::sleep_for(std::chrono::milliseconds(60)); времени ожидания после операции однократного использования / производства. Я увеличил 10 мс до 60 мс. Тогда код работает намного лучше. Теперь это редко терпит неудачу.

Я думаю, что код не заканчивается, потому что во время работы тупик. Я нашел причину, которая может привести к взаимоблокировке в Wiki: тупик возникает, когда процесс или поток переходит в состояние ожидания, поскольку запрошенный системный ресурс удерживается другим ожидающим процессом, который, в свою очередь, ожидает другого ресурса, удерживаемого другим ожидающим процессом. Если процесс не может изменить свое состояние на неопределенный срок, поскольку запрашиваемые им ресурсы используются другим ожидающим процессом, то говорят, что система находится в тупике.

Итак, возникает вопрос: действительно ли этотупик? Как избежать тупика? что я должен отредактировать, чтобы улучшить мой код?

Вот новый код:

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

using namespace std;

std::mutex mu; //mutex
//std::unique_lock<mutex> locker(mu); //unique_lock
std::condition_variable cond;

std::mutex m_mutex;

int consumerRunTurns = 0;

class Buffer {
public:
Buffer():count(0) {}
void put();
int get();
bool isBufferEmpty();
bool isBufferFull();
private:
int count;
}; //end class Buffer

//Thread Class and Functions inside:
class Consumer{
public:
    Consumer(Buffer* cbptr);
    void cons();
    bool isConsumerDead();
private:
    Buffer* BufferPtr;
};

class Producer{
public:
    Producer(Buffer* pbptr, Consumer* coptr);
    void prods();
private:
    Buffer* BufferPtr;
    Consumer* ConsumerPtr;
};

int main(){
    Buffer* tc = new Buffer();
    Consumer* cdtc = new Consumer(tc);
    Producer* pdtc = new Producer(tc,cdtc);
    std::thread t[5]; //declare an array of thread objects
    int i = 0;
    for(; i<3; ++i){
        t[i]=std::thread(&Producer::prods,std::ref(pdtc));
    }
    for(; i<5; ++i){
        t[i]=std::thread(&Consumer::cons,std::ref(cdtc));
    }

    cout << "Debug1" << endl;
    cout << "Debug1" << endl;
    cout << "Debug1" << endl;

    for(i = 0; i<5; ++i){
        t[i].join();
        cout << "now i is :" << i << endl;
    }

    cout << "All threads terminated" << endl;
    return 0;
}

void Buffer::put(){
    std::unique_lock<mutex> locker(mu); //unique_lock
    if(consumerRunTurns>=200){
            cond.notify_one();
            return;
            }
    while(isBufferFull()==1){
        if(consumerRunTurns>=200){
            cond.notify_one();
            break;
        }
        cond.wait(locker);
    }
    if(consumerRunTurns>=200){
            return;
    }
    if(isBufferFull()==0)
        cond.notify_one();
    ++count;
    std::cout << "producer thread" << this_thread::get_id() << ",count = " << count << endl;
}
int Buffer::get(){
    std::unique_lock<mutex> locker(mu); //unique_lock
    while(isBufferEmpty()==1){
        cond.wait(locker);
    }
    if(isBufferEmpty()==0)
        cond.notify_one();
    --count;
    std::cout << "consumer thread" << this_thread::get_id() << ",count = " << count << endl;
    return count;
}
bool Buffer::isBufferEmpty(){
    if(count <=0){
        std::cout << "buffer is empty, consumer thread" << this_thread::get_id() << " is about to suspend." << endl;
        return 1;
    }
    else
        return 0;
}
bool Buffer::isBufferFull(){
    if(count >=10){
        std::cout << "buffer is full, producer thread" << this_thread::get_id() << " is about to suspend." << endl;
        return 1;
    }
    else
        return 0;
}

Consumer::Consumer(Buffer* cbptr):BufferPtr(cbptr){
}

bool Consumer::isConsumerDead(){
    if(consumerRunTurns >= 200){
        cout << "consumer Dead" << endl;
        cout << "consumer Dead" << endl;
        cout << "consumer Dead" << endl;
        return 1;
    }
    else
        return 0;
}

void Consumer::cons(){
    for(int i = 0; i<100; ++i){
        BufferPtr->get();
        std::this_thread::sleep_for(std::chrono::milliseconds(60));
    }
    consumerRunTurns+=100;
}

Producer::Producer(Buffer* pbptr, Consumer* coptr):BufferPtr(pbptr), ConsumerPtr(coptr){}

void Producer::prods(){
    for(int i = 0; i<100; ++i){
        if(ConsumerPtr->isConsumerDead()){
            break;
        }
        BufferPtr->put();
        std::this_thread::sleep_for(std::chrono::milliseconds(60));
    }
}

Предыдущий код:

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

using namespace std;

std::mutex mu; //mutex
//std::unique_lock<mutex> locker(mu); //unique_lock
std::condition_variable cond;

std::mutex m_mutex;

class Buffer {
public:
Buffer():count(0) {}
void put();
int get();
bool isBufferEmpty();
bool isBufferFull();
private:
int count;
}; //end class Buffer

//Thread Class and Functions inside:
class Consumer{
public:
    Consumer(Buffer* cbptr);
    void cons();
    bool isConsumerDead();
private:
    Buffer* BufferPtr;
    int consumerDeadFlag;
};

class Producer{
public:
    Producer(Buffer* pbptr, Consumer* coptr);
    void prods();
private:
    Buffer* BufferPtr;
    Consumer* ConsumerPtr;
};

int main(){
    Buffer* tc = new Buffer();
    Consumer* cdtc = new Consumer(tc);
    Producer* pdtc = new Producer(tc,cdtc);
    std::thread t[5]; //declare an array of thread objects
    int i = 0;
    for(; i<3; ++i){
        t[i]=std::thread(&Producer::prods,std::ref(pdtc));
    }
    for(; i<5; ++i){
        t[i]=std::thread(&Consumer::cons,std::ref(cdtc));
    }

    cout << "Debug1" << endl;
    cout << "Debug1" << endl;
    cout << "Debug1" << endl;

    for(i = 0; i<5; ++i){
        t[i].join();
        cout << "now i is :" << i << endl;
    }

    cout << "All threads terminated" << endl;
    return 0;
}

void Buffer::put(){
    std::unique_lock<mutex> locker(mu); //unique_lock
    while(isBufferFull()==1){
        cond.wait(locker);
    }
    if(isBufferFull()==0)
        cond.notify_one();
    ++count;
    std::cout << "producer thread" << this_thread::get_id() << ",count = " << count << endl;
}
int Buffer::get(){
    std::unique_lock<mutex> locker(mu); //unique_lock
    while(isBufferEmpty()==1){
        cond.wait(locker);
    }
    if(isBufferEmpty()==0)
        cond.notify_one();
    --count;
    std::cout << "consumer thread" << this_thread::get_id() << ",count = " << count << endl;
    return count;
}
bool Buffer::isBufferEmpty(){
    if(count <=0){
        std::cout << "buffer is empty, consumer thread" << this_thread::get_id() << " is about to suspend." << endl;
        return 1;
    }
    else
        return 0;
}
bool Buffer::isBufferFull(){
    if(count >=10){
        std::cout << "buffer is full, producer thread" << this_thread::get_id() << " is about to suspend." << endl;
        return 1;
    }
    else
        return 0;
}

Consumer::Consumer(Buffer* cbptr):BufferPtr(cbptr){
    consumerDeadFlag = 0;
}

bool Consumer::isConsumerDead(){
    if(consumerDeadFlag > 1){
        cout << "consumer Dead" << endl;
        cout << "consumer Dead" << endl;
        cout << "consumer Dead" << endl;
        return 1;
    }
    else
        return 0;
}

void Consumer::cons(){
    for(int i = 0; i<100; ++i){
        BufferPtr->get();
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
    ++consumerDeadFlag;
}

Producer::Producer(Buffer* pbptr, Consumer* coptr):BufferPtr(pbptr), ConsumerPtr(coptr){}

void Producer::prods(){
    for(int i = 0; i<100; ++i){
        if(ConsumerPtr->isConsumerDead()){
            break;
        }
        BufferPtr->put();
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
}

Вывод:

producer threadDebug12,count = 1
producer thread4,count = 2

producer thread3,count = 3
Debug1
Debug1
consumer thread5,count = 2
consumer thread6,count = 1
producer thread2,count = 2
consumer thread5,count = 1
producer thread4,count = 2
producer thread3,count = 3
consumer thread6,count = 2
producer thread2,count = 3
producer thread4,count = 4
producer thread3,count = 5
consumer thread5,count = 4
consumer thread6,count = 3
producer thread2,count = 4
producer thread4,count = 5
consumer thread5,count = 4
producer thread3,count = 5
consumer thread6,count = 4
producer thread2,count = 5
consumer thread5,count = 4
producer thread4,count = 5
producer thread3,count = 6
consumer thread6,count = 5
producer thread2,count = 6
producer thread4,count = 7
producer thread3,count = 8
consumer thread5,count = 7
consumer thread6,count = 6
producer thread2,count = 7
consumer thread5,count = 6
producer thread4,count = 7
producer thread3,count = 8
consumer thread6,count = 7
producer thread2,count = 8
consumer thread5,count = 7
producer thread4,count = 8
producer thread3,count = 9
consumer thread6,count = 8
producer thread2,count = 9
producer thread4,count = 10
consumer thread5,count = 9
producer thread3,count = 10
consumer thread6,count = 9
producer thread2,count = 10
buffer is full, producer thread4 is about to suspend.
consumer thread5,count = 9
producer thread4,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread6,count = 9
producer thread3,count = 10
buffer is full, producer thread2 is about to suspend.
buffer is full, producer thread4 is about to suspend.
consumer thread5,count = 9
producer thread2,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread6,count = 9
producer thread4,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread5,count = 9
producer thread3,count = 10
buffer is full, producer thread2 is about to suspend.
buffer is full, producer thread4 is about to suspend.
consumer thread6,count = 9
producer thread2,count = 10
buffer is full, producer thread4 is about to suspend.
consumer thread5,count = 9
producer thread4,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread6,count = 9
producer thread3,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread5,count = 9
producer thread2,count = 10
buffer is full, producer thread4 is about to suspend.
consumer thread6,count = 9
producer thread3,count = 10
buffer is full, producer thread4 is about to suspend.
buffer is full, producer thread2 is about to suspend.
consumer thread5,count = 9
producer thread4,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread6,count = 9
producer thread2,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread5,count = 9
producer thread3,count = 10
buffer is full, producer thread4 is about to suspend.
buffer is full, producer thread2 is about to suspend.
consumer thread6,count = 9
producer thread4,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread5,count = 9
producer thread2,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread6,count = 9
producer thread3,count = 10
buffer is full, producer thread4 is about to suspend.
consumer thread5,count = 9
producer thread4,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread6,count = 9
producer thread2,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread5,count = 9
producer thread3,count = 10
buffer is full, producer thread4 is about to suspend.
buffer is full, producer thread2 is about to suspend.
consumer thread6,count = 9
producer thread4,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread5,count = 9
producer thread2,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread6,count = 9
producer thread4,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread5,count = 9
producer thread3,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread6,count = 9
producer thread4,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread5,count = 9
producer thread2,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread6,count = 9
producer thread3,count = 10
buffer is full, producer thread4 is about to suspend.
consumer thread5,count = 9
producer thread2,count = 10
buffer is full, producer thread4 is about to suspend.
buffer is full, producer thread3 is about to suspend.
consumer thread6,count = 9
producer thread4,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread5,count = 9
producer thread3,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread6,count = 9
producer thread4,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread5,count = 9
producer thread3,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread6,count = 9
producer thread4,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread5,count = 9
producer thread2,count = 10
buffer is full, producer thread3 is about to suspend.
buffer is full, producer thread4 is about to suspend.
consumer thread6,count = 9
producer thread3,count = 10
buffer is full, producer thread4 is about to suspend.
consumer thread5,count = 9
producer thread4,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread6,count = 9
producer thread2,count = 10
buffer is full, producer thread3 is about to suspend.
buffer is full, producer thread4 is about to suspend.
consumer thread5,count = 9
producer thread3,count = 10
buffer is full, producer thread4 is about to suspend.
consumer thread6,count = 9
producer thread4,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread5,count = 9
producer thread3,count = 10
buffer is full, producer thread2 is about to suspend.
buffer is full, producer thread4 is about to suspend.
consumer thread6,count = 9
producer thread2,count = 10
buffer is full, producer thread4 is about to suspend.
buffer is full, producer thread3 is about to suspend.
consumer thread5,count = 9
producer thread4,count = 10
buffer is full, producer thread3 is about to suspend.
buffer is full, producer thread2 is about to suspend.
consumer thread6,count = 9
producer thread3,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread5,count = 9
producer thread2,count = 10
buffer is full, producer thread4 is about to suspend.
consumer thread6,count = 9
producer thread4,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread5,count = 9
producer thread3,count = 10
buffer is full, producer thread2 is about to suspend.
buffer is full, producer thread4 is about to suspend.
consumer thread6,count = 9
producer thread2,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread5,count = 9
producer thread4,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread6,count = 9
buffer is full, producer thread4 is about to suspend.
consumer thread6,count = 9
producer thread4,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread5,count = 9
producer thread3,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread6,count = 9
producer thread2,count = 10
buffer is full, producer thread4 is about to suspend.
consumer thread5,count = 9
producer thread3,count = 10
buffer is full, producer thread4 is about to suspend.
consumer thread6,count = 9
producer thread4,count = 10
buffer is full, producer thread2 is about to suspend.
buffer is full, producer thread3 is about to suspend.
consumer thread5,count = 9
producer thread2,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread6,count = 9
producer thread3,count = 10
buffer is full, producer thread4 is about to suspend.
consumer thread5,count = 9
producer thread4,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread6,count = 9
producer thread2,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread5,count = 9
producer thread3,count = 10
buffer is full, producer thread4 is about to suspend.
consumer thread6,count = 9
producer thread2,count = 10
buffer is full, producer thread4 is about to suspend.
buffer is full, producer thread3 is about to suspend.
consumer thread5,count = 9
producer thread4,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread6,count = 9
producer thread3,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread5,count = 9
producer thread2,count = 10
buffer is full, producer thread4 is about to suspend.
consumer thread6,count = 9
producer thread4,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread5,count = 9
producer thread3,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread6,count = 9
producer thread2,count = 10
buffer is full, producer thread4 is about to suspend.
buffer is full, producer thread3 is about to suspend.
consumer thread5,count = 9
producer thread4,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread6,count = 9
producer thread3,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread5,count = 9
producer thread2,count = 10
buffer is full, producer thread4 is about to suspend.
consumer thread6,count = 9
producer thread4,count = 10
buffer is full, producer thread3 is about to suspend.
buffer is full, producer thread2 is about to suspend.
consumer thread5,count = 9
producer thread3,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread6,count = 9
producer thread4,count = 10
buffer is full, producer thread2 is about to suspend.
buffer is full, producer thread3 is about to suspend.
consumer thread5,count = 9
producer thread2,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread6,count = 9
producer thread3,count = 10
buffer is full, producer thread4 is about to suspend.
consumer thread5,count = 9
producer thread4,count = 10
buffer is full, producer thread2 is about to suspend.
buffer is full, producer thread3 is about to suspend.
consumer thread6,count = 9
producer thread2,count = 10
buffer is full, producer thread3 is about to suspend.
buffer is full, producer thread4 is about to suspend.
consumer thread5,count = 9
producer thread3,count = 10
buffer is full, producer thread4 is about to suspend.
buffer is full, producer thread2 is about to suspend.
consumer thread6,count = 9
producer thread4,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread5,count = 9
producer thread2,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread6,count = 9
producer thread3,count = 10
buffer is full, producer thread4 is about to suspend.
consumer thread5,count = 9
producer thread4,count = 10
buffer is full, producer thread2 is about to suspend.
buffer is full, producer thread3 is about to suspend.
consumer thread6,count = 9
producer thread2,count = 10
consumer thread6,count = 9
producer thread4,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread5,count = 9
producer thread3,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread6,count = 9
producer thread2,count = 10
buffer is full, producer thread4 is about to suspend.
consumer thread5,count = 9
producer thread4,count = 10
buffer is full, producer thread3 is about to suspend.
buffer is full, producer thread2 is about to suspend.
consumer thread6,count = 9
producer thread3,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread5,count = 9
producer thread4,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread6,count = 9
producer thread2,count = 10
buffer is full, producer thread3 is about to suspend.
buffer is full, producer thread4 is about to suspend.
consumer thread5,count = 9
producer thread3,count = 10
buffer is full, producer thread4 is about to suspend.
buffer is full, producer thread2 is about to suspend.
consumer thread6,count = 9
producer thread4,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread5,count = 9
producer thread2,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread6,count = 9
producer thread3,count = 10
buffer is full, producer thread4 is about to suspend.
buffer is full, producer thread2 is about to suspend.

и т. Д. (из-за ограничения слов)

consumer Dead
consumer Dead
consumer Dead

1 Ответ

0 голосов
/ 04 октября 2019

Прежде всего, std::thread::join не уничтожает поток - он ожидает завершения потока.

У вас есть 3 потока производителей (каждый производит100 штук), но только 2 потребительских потока (по 100 штук в каждой). Это означает, что излишки производятся на 100 единиц. Добавьте к этому буфер вместимостью всего 10 единиц, и ваши производители никогда не закончат самостоятельно, пока потребители не закончили .

Это означает, что вам нужен механизм для завершения производителязацикливается рано (т. е. как только завершаются циклы потребителя).

Однако ваша попытка решить эту проблему оставила вас открытым для бесконечно заблокированного потока (аналогично тупику, но невполне). Сценарий, который выпадает на меня (что не означает, что он единственный), это когда один или несколько потоков производителей заблокированы на cond.wait(locker);Buffer::put), но все потребительские потоки закончились (то есть cond.notify_one(); в Buffer::get больше не будет)Таким образом, поток (ы) производителя будут ждать вечно, и соответствующие join (s) не будут возвращаться.

Чтобы избежать этой ситуации, вы можете использовать вместо std::condition_variable::wait_forstd::condition_variable::wait, чтобы избежать ожидания блокировки. Каждый раз, когда происходит тайм-аут, у вас есть возможность проверить, закончились ли потребители, и завершить поток производителей, когда это так.

Способ проверки того, закончились ли потоки потребителей, также может быть улучшен. Один из вариантов - сначала join 2 пользовательских потока. Когда оба потребительских потока объединяются, сигнализируйте всем потокам производителя, что они также должны завершиться, затем join потоков производителя. Рассматриваемый сигнал может быть, например. std::atomic<bool>, который проверяется на каждой итерации производителя. Это будет работать лучше всего, если BufferPtr->put(); будет изменено на BufferPtr->try_put();, которое либо вставляет элемент в буфер, либо возвращает сразу (или по истечении времени ожидания, если вы предпочитаете), когда буфер заполнен.

Обратите внимание, что в вашем коде есть и другие проблемы (не в последнюю очередь из-за того, что вы используете один и тот же condition_variable для сигнализации как «не пустой», так и «не полный»), но они не входят в сферу этого вопроса.

...