Многопоточность и общий ресурс: периодически копируйте данные из буфера (структура данных) в файл, используя C++
Мой код имеет структуру данных, скажем, например, "вектор vecor строк". У меня есть 2 потока:
THread 1 записывает данные в эту структуру данных (буфер в RAM).
Параллельно работающий поток 2, который должен копировать данные из вышеприведенного буфера, т.е. структуру данных, в файл за каждые "х" миллисекунд.
Мне интересно, как бы я достиг этого в C++? Следует учитывать ключевые моменты в постановке задачи, такие как:
a) Копирование из буфера в файл должно происходить только один раз за "X" миллисекунд.
б) Синхронизация между обоими потоками.
РЕДАКТИРОВАНИЕ ЗАПРОСА С БОЛЕЕ ДЕТАЛЯМИ НА СПРОС
Я хочу создать библиотеку (*.lib), и эта библиотека предоставляет некоторые API, следовательно, она получает входные данные из EXE или любого объекта, который использует мою библиотеку через эти API. Скажем, данные, полученные моей библиотекой, представлены в виде вектора строк.
FillLibraryDataStructure(std::vector<std::string>); // is the API of this library. Any app can call this API & pass a vector of string to this library.
Example app code:
for(int i=100; i<100;i))
{
std::vector<std::string> vec = GetVectorOfString(); // GetVectorOfString from business logic
FillLibraryDataStructure(vec);
}
Код библиотеки имеет общий ресурс:
// Within library I've a 2D vector i.e. vector of vector of strings where all the vector of strings passed by application to this librray are added as a new row in vecofvecofstr.
ОБЩИЙ РЕСУРС:
std::vector<std::vector<string>> vecofvecofstr;
THREAD 1: копирует данные, полученные из API, в структуру данных, т.е. вектор вектора строк.
vecofvecofstr.push_back(vec);
THREAD 2: копирует содержимое этого вектора вектора строки (который был записан в 1-м потоке) в файлы (XML, HTML и т. Д.) За каждые "X" милисекунд.
Еще несколько замечаний о потоке 1: 1) Поток 1 должен работать непрерывно, т. Е. Когда приложение вызывает API, полученные данные должны быть помещены в структуру данных vecofvecofstr. 2) Через миллисекунды "X" копирования данных в буфер, 2-й поток должен начать работу и скопировать все содержимое, которое было выгружено в буфер до даты. Опять после тысячелетий "X" поток 2 должен остановиться и ждать "X" мс.
Как мне этого добиться. Здесь 1-й поток - это поток по умолчанию, в котором будет выполняться код моей библиотеки. Как мне добиться этого с помощью C++?
2 ответа
Вы могли бы использовать std::mutex
а также std::condition_variable>
в ваших интересах. И двойной буфер будет держать блокировку до минимума.
std::condition_variable>
является ближайшей вещью к событию, которое может предложить стандартное устройство, его использование немного надумано, но оно работает.
В приведенном ниже примере используется двойной буфер, поэтому вы можете продолжать буферизацию данных, пока поток 2 сохраняет данные в файл, без блокировки.
Переменная std:condition_variable используется для того, чтобы ваше приложение могло выйти без ожидания. Это необходимо только в том случае, если вы хотите, чтобы приложение быстро завершалось, иначе вы можете использовать таймер. Призыв к notify_all()
предотвратит тайм-аут wait_for() и немедленно разбудит поток записи, чтобы он мог завершиться без ожидания истечения времени ожидания. См. Ссылку по адресу: http://en.cppreference.com/w/cpp/thread/condition_variable
Заголовок:
#include <mutex>
// a generic double buffer
template<typename _Data>
class DoubleBuffer
{
private:
std::mutex mutex_;
std::vector<std::vector<_Data>> storeBuffer_;
std::vector<std::vector<_Data>> saveBuffer_;
public:
void lock() { mutex_.lock(); }
void unlock() { mutex_.unlock();}
auto& GetStoreBuffer() { return storeBuffer_; }
auto& GetSaveBuffer() { return saveBuffer_; }
auto& Swap()
{
std::lock_guard<std::mutex> lock(mutex_);
std::swap(storeBuffer_, saveBuffer_);
}
};
В вашей библиотеке:
#include <condition_variable>
#include <thread>
#include <chrono>
#include <mutex>
#include <vector>
#include <string>
// As an example, could be inside a class, or struct
static std::condition_variable exiting;
static std::mutex lk_exiting;
static DoubleBuffer<std::string> yourBuffer;
void FillLibraryDataStructure(std::vector<std::string> strings)
{
// the lock is only for the duration of a swap - very short at worst.
std::lock_guard<DoubleBuffer<std::string>> lock(yourBuffer);
yourBuffer.GetStoreBuffer().emplace_back(strings);
}
void StoreLoop()
{
for(;;)
{
{ // wait_for() unlocks lk_exiting, doc says lock should
// be set before cv is triggered.
std::unique_lock<std::mutex> lk_exiting;
if (std::cv_status::no_timeout == exiting.wait_for(lk_exiting, 60s))
break; // app is exiting
}
yourBuffer.Swap();
auto& stringsToSave = GetSaveBuffer();
// save... You do have plenty of time.
}
}
// as an example. A destructor would be a good place for this
void Exit_Application()
{
// stops the wait_for operation in StoreLoop()
exiting.notify_all();
}
В качестве общего ответа на ваш общий вопрос, есть два возможных варианта:
1 - использовать некоторое время ожидания, сигнальные команды для сна и пробуждения потоков параллельно
2- использовать немного сна, чтобы обеспечить X миллисекунд в потоке чтения
Если вам нужен лучший ответ, дайте больше деталей