Реализация двойной буферизации с использованием фьючерсов и обещаний с использованием С++ 11

Я начал изучать многопоточность и наткнулся на фьючерсы и промисы для синхронизации потоков по общим ресурсам. Итак, я подумал о реализации известной проблемы двойной буферизации с использованием фьючерсов и обещаний (один производитель и один потребитель). Основная методология, о которой я подумал, такова:

Тема производителя:

loop:    
    locks_buffer1_mutex    
    fills_buffer1   
    unlocks_buffer1_mutex    
    passes number 1 to Consumer thread using promise.setvalue()    
    locks_buffer2_mutex    
    fills_buffer2   
    unlocks_buffer2_mutex    
    passes number 2 to Consumer thread using promise.setvalue()
back_to_loop

Потребительская нить:

loop:
   wait_for_value_from_promise
   switch
      case 1:
         lock_buffer1_mutex
          process(buffer1)
         unlock_buffer1_mutex
         print_values
         break
       case 2:
         lock_buffer2_mutex
         process(buffer2)
         unlock_buffer2_mutex
         print_values
         break
back_to_loop

Вот код:

#include <iostream>
#include <thread>
#include <vector>
#include <future>
#include <mutex>
#include <iterator>


std::mutex print_mutex;
std::mutex buffer1_mutex;
std::mutex buffer2_mutex;

std::vector<int> buffer1;
std::vector<int> buffer2;

bool notify;


void DataAcquisition(std::promise<int> &p)
{
    std::this_thread::sleep_for(std::chrono::seconds(2));
    while(true)
    {
        {
            std::lock_guard<std::mutex> buff1_lock(buffer1_mutex);
            for(int i=0;i<200;i++)
            {
                buffer1.push_back(i);
            }
        }
        p.set_value(1);
        {
            std::lock_guard<std::mutex> buff2_lock(buffer2_mutex);
            for(int i=0;i<200;i++)
            {
                buffer2.push_back(199-i);
            }
        }
        p.set_value(2);
    }
}

void DataExtraction(std::future<int> &f)
{
    std::vector<int>::const_iterator first,last;
    std::vector<int> new_vector;
    std::ostream_iterator<int> outit(std::cout, " ");

    while(true)
    {
        int i = f.get();
        std::cout << "The value of i is :" << i << std::endl;
        switch(i)
        {
            case 1:
                {
                    std::lock_guard<std::mutex> buff1_lock(buffer1_mutex);
                    first = buffer1.begin();
                    last = first + 10;
                }
                new_vector = std::vector<int>(first,last);
                {
                    std::lock_guard<std::mutex> print_lock(print_mutex);
                    std::copy(new_vector.begin(),new_vector.end(),outit);
                }
                break;
              case 2:
                {
                    std::lock_guard<std::mutex> buff2_lock(buffer2_mutex);
                    first = buffer2.begin();
                    last = first + 10;
                }
                new_vector = std::vector<int>(first,last);
                {
                    std::lock_guard<std::mutex> print_lock(print_mutex);
                    std::copy(new_vector.begin(),new_vector.end(),outit);
                }
                break;
           }
    }
}

int main()
{
    std::promise<int> p;
    std::future<int> f = p.get_future();


    std::thread thread1(DataAcquisition,std::ref(p));
    std::thread thread2(DataExtraction,std::ref(f));

    thread1.join();
    thread2.join();

    return 0;
}

Когда я выполняю этот код, я столкнулся с его гигантской проблемой, о которой я совершенно не подозреваю.

terminate called after throwing an instance of 'std::future_error' terminate called recursively
  what(): 0 1 2 3 4 5 6 7 8 9 Promise already satisfied
Press <RETURN> to close the window

Я гуглил об этой ошибке, предлагается связать переключатель -lpthread во время компоновки и компиляции. но не смог решить проблему.

Пожалуйста, помогите мне, где я иду не так..


person jeldikk    schedule 28.04.2015    source источник
comment
Для того, что вы пытаетесь сделать, не будет ли достаточно атома, чтобы указать последний заполненный буфер? Обратите внимание, что нет никакой гарантии, что «Потребитель» увидит все кадры, созданные «Производителем». Это легко увидеть интуитивно, если вы представляете, что «печать значений» медленная. Потребитель не удерживает блокировки во время «печати значений», а «Производитель» может создавать любое количество кадров и перезаписывать их. Даже если вы не возражаете против «пропуска кадров», более тонкое сотрудничество будет означать, что вы не тратите впустую циклы ЦП на создание вывода, который никто никогда не увидит.   -  person Persixty    schedule 28.04.2015
comment
По аналогичному аргументу Consumer может обрабатывать один и тот же кадр дважды.   -  person Persixty    schedule 28.04.2015


Ответы (1)


Вы не можете вызывать set_value на promise больше, чем один раз, что проиллюстрировано следующим кодом:

#include <future>

int main() {
    std::promise<int> p;
    p.set_value(1);
    p.set_value(2); // Promise already satisfied
}

Придется искать другой подход. Например, вы можете использовать два std::condition_variable — установите их в производителе , и дождаться их в потребителе.

person Anton Savin    schedule 28.04.2015
comment
Привет, Антон. Ты имеешь в виду, что мы не можем вызывать set_value для промиса более одного раза за все время выполнения задачи...?? Предложение, которое вы дали относительно чтения буферов, в свою очередь, я не мог точно понять, что вы предлагаете мне сделать. - person jeldikk; 28.04.2015
comment
@ user1511151 да, как только значение установлено, вы больше не можете этого делать. Что касается, в свою очередь, кажется, что это не будет легко работать, вы можете использовать условные переменные, см. Мое обновление. - person Anton Savin; 28.04.2015
comment
Правильно, обещание — это просто значение + время, когда значение есть, его невозможно установить снова — это все равно, что пытаться установить 5 дважды. - person Benjamin Gruenbaum; 28.04.2015
comment
@Anton, вы хотите сказать, что обещание используется только для изменения состояния, но возвращает значение типа. У нас есть condition_variable для изменения состояния. Тогда что обещает уникальная природа..?? - person jeldikk; 29.04.2015