2016-08-31 6 views
2


Я пытаюсь сделать много ошибок, чтобы изучить параллелизм в C++ 11. Я должен спросить об этом,

Вот что должен сделать этот: Одна очередь и три потока, одна должна положить целое число в очередь, другие двойки предполагают соответственно увеличить s1, s2 на выталкивая очередь, чтобы я мог получить общую сумму чисел, которые были в очереди. Чтобы сделать это проще, я помещал от 1 до 10 чисел в очередь.

Но иногда это работает, и иногда кажется, что существует бесконечный цикл: в чем причина?

C++ 11 condtional variable

#include <queue> 
#include <memory> 
#include <mutex> 
#include <thread> 
#include <iostream> 
#include <condition_variable> 
#include <string> 

class threadsafe_queue { 
private: 
    mutable std::mutex mut; 
    std::queue<int> data_queue; 
    std::condition_variable data_cond; 
    std::string log; //just to see what is going on behind 
    bool done; 

public: 
    threadsafe_queue(){ 
     log = "initializing queue\n"; 
     done = false; 
    } 
    threadsafe_queue(threadsafe_queue const& other) { 
     std::lock_guard<std::mutex> lk(other.mut); 
     data_queue = other.data_queue; 
    } 
    void set_done(bool const s) { 
     std::lock_guard<std::mutex> lk(mut); 
     done = s; 
    } 
    bool get_done() { 
     std::lock_guard<std::mutex> lk(mut); 
     return done; 
    } 
    void push(int new_value) { 
     std::lock_guard<std::mutex> lk(mut); 
     log += "+pushing " + std::to_string(new_value) + "\n"; 
     data_queue.push(new_value); 
     data_cond.notify_one(); 
    } 
    void wait_and_pop(int& value) { 
     std::unique_lock<std::mutex> lk(mut); 
     data_cond.wait(lk, [this]{return !data_queue.empty();}); 
     value = data_queue.front(); 
     log += "-poping " + std::to_string(value) + "\n"; 
     data_queue.pop(); 
    } 
    std::shared_ptr<int> wait_and_pop() { 
     std::unique_lock<std::mutex> lk(mut); 
     data_cond.wait(lk, [this]{return !data_queue.empty();}); 
     std::shared_ptr<int> res(std::make_shared<int>(data_queue.front())); 
     log += "- popping " + std::to_string(*res) + "\n"; 
     data_queue.pop(); 
     return res; 
    } 
    bool try_pop(int& value) { 
     std::lock_guard<std::mutex> lk(mut); 
     if (data_queue.empty()) { 
      log += "tried to pop but it was empty\n"; 
      return false; 
     } 
     value = data_queue.front(); 
     log += "-popping " + std::to_string(value) + "\n"; 
     data_queue.pop(); 
     return true; 
    } 
    std::shared_ptr<int> try_pop() { 
     std::lock_guard<std::mutex> lk(mut); 
     if (data_queue.empty()) { 
      log += "tried to pop but it was empty\n"; 
      return std::shared_ptr<int>(); 
     } 
     std::shared_ptr<int> res(std::make_shared<int>(data_queue.front())); 
     log += "-popping " + std::to_string(*res) + "\n"; 
     data_queue.pop(); 
     return res; 
    } 
    bool empty() const { 
     std::lock_guard<std::mutex> lk(mut); 
     //log += "checking the queue if it is empty\n"; 
     return data_queue.empty(); 
    } 

    std::string get_log() { 
     return log; 
    } 

}; 

threadsafe_queue tq; 
int s1, s2; 

void prepare() { 
    for (int i = 1; i <= 10; i++) 
     tq.push(i); 
    tq.set_done(true); 
} 

void p1() { 
    while (true) { 
     int data; 
     tq.wait_and_pop(data); 
     s1 += data; 
     if (tq.get_done() && tq.empty()) break; 
    } 
} 

void p2() { 
    while (true) { 
     int data; 
     tq.wait_and_pop(data); 
     s2 += data; 
     if (tq.get_done() && tq.empty()) break; 
    } 
} 

int main(int argc, char *argv[]) { 
    std::thread pp(prepare); 
    std::thread worker(p1); 
    std::thread worker2(p2); 
    pp.join(); 
    worker.join(); 
    worker2.join(); 

    std::cout << tq.get_log() << std::endl; 
    std::cout << s1 << " " << s2 << std::endl; 
    return 0; 
} 
+0

Возможно, вам необходимо уведомить 'data_cond' при установке' done' и проверить его в состоянии 'data_cond.wait'. – dewaffled

+0

Я думаю, что вы получаете тупик, если 'wait_and_pop()' вызывается до 'push()', поскольку 'push()' не может пройти мимо мьютекса, чтобы вызвать 'notify_one', чтобы освободить' wait_and_pop() 'от его ожидания. Я думаю, вы должны 'wait()' * перед тем, как захватить блокировку мьютекса. –

+0

@ KenY-N, да, это то, что я подозревал. Но как я могу убедиться, что push() должен работать раньше всего? – Nano

ответ

1

Посмотрите на функции p1 линии 5

если (tq.get_done() & & tq.empty)() перерыв;

Итак, вы проверили очередь, если она была пустой. Не было. Теперь вы петляете и вводите

tq.wait_and_pop (data);

, где вы найдете

data_cond.wait (Л.К., [это] {вернуть data_queue.empty (!);});

который является по существу

в то время (data_queue.empty()) { ожидание (лк); }

обратите внимание на недостающее '!'.

Теперь ваш поток сидит там и ждет, чтобы очередь не была пуста, чего никогда не произойдет, потому что идентификатор производителя заполняет очередь. Нить никогда не будет соединяться.

Есть много способов исправить это. Я уверен, что вы найдете его по своему усмотрению.

+0

Ах да, это правда. Если 'p2' опустошает очередь между' p1' 'tq.empty()' и следующей 'tq.wait_and_pop (data);' будет другая зависает. –

+0

круто! спасибо – Nano

+0

@Nano: Если мой ответ удовлетворил ваш вопрос, подумайте о добавлении зеленого маленького клеща рядом с моим ответом. ти – PanicSheep