многопоточность — использование стека c ++ с двумя потоками

Я искал в Интернете, но не могу найти хороший пример того, что я пытаюсь достичь. Используя c ++, я работаю с визуализацией для Kodi (приложение с открытым исходным кодом для медиацентра). Эта визуализация отправляет данные в виде http-клиента на мост Philips Hue, http-сервер, для изменения цветов и других атрибутов источников света в соответствии с музыкой. Я использую cURL для обработки запросов http.

Использование одного потока приводит к проблемам, при которых cURL требует времени для выполнения своей работы и получения ответов от моста Philips Hue (http-сервер). Это блокирует визуализацию на экране и иногда вывод звука. Вы можете увидеть задержки здесь: YouTube видео визуализации.

То, как надстройки структурированы в Kodi, означает отсутствие «основной» функции. Так что для многопоточности я не могу найти хорошую конструкцию, так как большинство примеров в Интернете создают поток в main и присоединяются к нему позже в main. На данный момент я пытаюсь что-то вроде этого:

  • функция запуска создает рабочий поток
  • рабочий поток ожидает, пока стек не будет заполнен функцией аудиоданных
  • как только стек заполнен, рабочий поток читает верхний элемент,
    отправляет запрос cURL и выскакивает из элемента
  • функция уничтожения должна присоединиться к потоку?

Я пробовал несколько методов для создания потока, но при отладке в Windows все они приводят к таким ошибкам, как:

First-chance exception at 0x0856335A (visualization.wavforhue.dll) in Kodi.exe: 0xC0000005: Access violation reading location 0xFEEEFEF6.
First-chance exception at 0x76CDC52F in Kodi.exe: Microsoft C++ exception: access_violation at memory location 0x003BEC5C.
First-chance exception at 0x76CDC52F in Kodi.exe: Microsoft C++ exception: access_violation at memory location 0x003BEC5C.

или же

Run-Time Check Failure #2 - Stack around the variable '_Now' was corrupted. в std::this_thread::sleep_for(std::chrono::seconds(2));,

или abort(); вызванный Kodi после start Функция завершается.


При включенных вызовах curl в приведенном ниже коде я получаю другую ошибку. Это происходит со второй попытки воспроизвести песню. Первая попытка успешна.

Ошибка The ordinal 4445 could not be located in the dynamic link library LIBEAY32.dll. Эта библиотека связана с SSL, который мой код не использует. Тем не менее, я должен как-то влиять на другие случаи скручивания в программе Kodi.

Интересная часть без скручиваемых звонков, код работает без ошибок. Я думаю, что если я смогу успешно решить проблему с LIBEAY32.dll, это можно пометить как решенную.

Вот полный код (уведомление GPL внизу для улучшения читабельности):

//--------------------------------------------------------------------------------------
#include <xbmc_vis_dll.h>
#include <stdio.h>
#ifdef _WIN32
#include <winsock2.h>
#endif
#include <curl/curl.h>
#include <string>
//------------------------------------------------------------------//------------------------------------------------------------------
#include <atomic>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
//----------------------------------------------------------------------// Thread initialization -------------------------------------------------
std::mutex gMutex;
std::condition_variable gThreadConditionVariable;
std::atomic<bool> gRunThread;
bool gReady;
std::thread gWorkerThread;
std::queue<int> gQueue;
// End thread initialization ---------------------------------------------void workerThread()
{
bool isEmpty;
std::string json;
// This thread comes alive when Create(), Start(), or AudioData()
// is invoked by the main program.
// It runs until Destroy() or Stop() is invoked.
while (gRunThread)
{
//check that an item is on the stack

{
std::lock_guard<std::mutex> lock(gMutex);
isEmpty = gQueue.empty();
}

if (isEmpty)
{
//Wait until AudioData() sends data.
std::unique_lock<std::mutex> lock(gMutex);
gThreadConditionVariable.wait(lock, []{return gReady; });
}
else
{
std::lock_guard<std::mutex> lock(gMutex);
int value = gQueue.front();
gQueue.pop();
}
if (!isEmpty)
{
/*
CURL *curl = curl_easy_init();
CURLcode res;
json = "{\"hue\":" + std::to_string(rand() % 60000) + "}";
// Now specify we want to PUT data, but not using a file, so it has to be a CUSTOMREQUEST
curl_easy_setopt(curl, CURLOPT_TCP_NODELAY, 1);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 3L);
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
//curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, noop_cb);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json.c_str());
// Set the URL that is about to receive our POST.
curl_easy_setopt(curl, CURLOPT_URL, "http://192.168.10.6/api/KodiVisWave/lights/3/state");
// Perform the request, res will get the return code
res = curl_easy_perform(curl);
// always cleanup curl
curl_easy_cleanup(curl);
*/
}
}
}//-- Create -------------------------------------------------------------------
// Called on load. Addon should fully initalize or return error status
//-----------------------------------------------------------------------------
extern "C" ADDON_STATUS ADDON_Create(void* hdl, void* props)
{
if (!props)
return ADDON_STATUS_UNKNOWN;

gRunThread = true;
gReady = false;

// Check if the thread is alive yet.
if (!gWorkerThread.joinable())
{
gWorkerThread = std::thread(&workerThread);
}

// Must initialize libcurl before any threads are started.
//curl_global_init(CURL_GLOBAL_ALL);

return ADDON_STATUS_OK;
}

//-- Start --------------------------------------------------------------------
// Called when a new soundtrack is played
//-----------------------------------------------------------------------------
extern "C" void Start(int iChannels, int iSamplesPerSec, int iBitsPerSample, const char* szSongName)
{
gRunThread = true;
// Check if the thread is alive yet.
if (!gWorkerThread.joinable())
{
gWorkerThread = std::thread(&workerThread);
}
}

//-- Audiodata ----------------------------------------------------------------
// Called by XBMC to pass new audio data to the vis
//-----------------------------------------------------------------------------
extern "C" void AudioData(const float* pAudioData, int iAudioDataLength, float *pFreqData, int iFreqDataLength)
{
// Processing audio data
if (rand() % 7 == 3)
{
std::lock_guard<std::mutex> lock(gMutex);
gQueue.push(1);
}

gRunThread = true;
// Check if the thread is alive yet.
if (!gWorkerThread.joinable())
{
gWorkerThread = std::thread(&workerThread);
}

// Send the curl calls to the worker thread
{
std::lock_guard<std::mutex> lock(gMutex);
gReady = true;
}
gThreadConditionVariable.notify_one();

}

//-- Stop ---------------------------------------------------------------------
// This dll must stop all runtime activities
// !!! Add-on master function !!!
//-----------------------------------------------------------------------------
extern "C" void ADDON_Stop()
{
gRunThread = false;
while (gWorkerThread.joinable())
{
gWorkerThread.join();
}
}

//-- Detroy -------------------------------------------------------------------
// Do everything before unload of this add-on
// !!! Add-on master function !!!
//-----------------------------------------------------------------------------
extern "C" void ADDON_Destroy()
{
gRunThread = false;
while (gWorkerThread.joinable())
{
gWorkerThread.join();
}
}

//-- Render -------------------------------------------------------------------
// Called once per frame. Do all rendering here.
//-----------------------------------------------------------------------------
extern "C" void Render()
{

}

//-- GetInfo ------------------------------------------------------------------
// Tell XBMC our requirements
//-----------------------------------------------------------------------------
extern "C" void GetInfo(VIS_INFO* pInfo)
{
pInfo->bWantsFreq = false;
pInfo->iSyncDelay = 0;
}

//-- OnAction -----------------------------------------------------------------
// Handle XBMC actions such as next preset, lock preset, album art changed etc
//-----------------------------------------------------------------------------
extern "C" bool OnAction(long flags, const void *param)
{
bool ret = false;
return ret;
}

//-- GetPresets ---------------------------------------------------------------
// Return a list of presets to XBMC for display
//-----------------------------------------------------------------------------
extern "C" unsigned int GetPresets(char ***presets)
{
return 0;
}

//-- GetPreset ----------------------------------------------------------------
// Return the index of the current playing preset
//-----------------------------------------------------------------------------
extern "C" unsigned GetPreset()
{
return 0;
}

//-- IsLocked -----------------------------------------------------------------
// Returns true if this add-on use settings
//-----------------------------------------------------------------------------
extern "C" bool IsLocked()
{
return false;
}

//-- GetSubModules ------------------------------------------------------------
// Return any sub modules supported by this vis
//-----------------------------------------------------------------------------
extern "C" unsigned int GetSubModules(char ***names)
{
return 0; // this vis supports 0 sub modules
}

//-- HasSettings --------------------------------------------------------------
// Returns true if this add-on use settings
// !!! Add-on master function !!!
//-----------------------------------------------------------------------------
extern "C" bool ADDON_HasSettings()
{
return false;
}

//-- GetStatus ---------------------------------------------------------------
// Returns the current Status of this visualization
// !!! Add-on master function !!!
//-----------------------------------------------------------------------------
extern "C" ADDON_STATUS ADDON_GetStatus()
{
return ADDON_STATUS_OK;
}

//-- GetSettings --------------------------------------------------------------
// Return the settings for XBMC to display
// !!! Add-on master function !!!
//-----------------------------------------------------------------------------
extern "C" unsigned int ADDON_GetSettings(ADDON_StructSetting ***sSet)
{
return 0;
}

//-- FreeSettings --------------------------------------------------------------
// Free the settings struct passed from XBMC
// !!! Add-on master function !!!
//-----------------------------------------------------------------------------

extern "C" void ADDON_FreeSettings()
{
}

//-- SetSetting ---------------------------------------------------------------
// Set a specific Setting value (called from XBMC)
// !!! Add-on master function !!!
//-----------------------------------------------------------------------------
extern "C" ADDON_STATUS ADDON_SetSetting(const char *strSetting, const void* value)
{
return ADDON_STATUS_OK;
}

//-- Announce -----------------------------------------------------------------
// Receive announcements from XBMC
// !!! Add-on master function !!!
//-----------------------------------------------------------------------------
extern "C" void ADDON_Announce(const char *flag, const char *sender, const char *message, const void *data)
{
}

/*
*      Copyright (C) 2008-2016 Team Kodi
*      http://kodi.tv
*
*  This Program is free software; you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation; either version 2, or (at your option)
*  any later version.
*
*  This Program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with XBMC; see the file COPYING.  If not, see
*  <http://www.gnu.org/licenses/>.
*
*/

Есть лучший способ сделать это? Если нет, что мне делать в функции уничтожения, чтобы уменьшить нарушение доступа?


Здесь приведена ссылка на крайне исходную версию исходного кода без сложной обработки звуковых данных, рендеринга графики и вызовов curl. Это все еще терпит неудачу с Run-Time Check Failure #2 - Stack around the variable '_Now' was corrupted. в std::this_thread::sleep_for(std::chrono::seconds(2));,


Редактировать: Код, указанный выше, должен работать сейчас. Я все еще испытываю отдельные проблемы с DirectX 11 и cURL в Windows 7 и 8.1, но OpenELEC, Ubuntu и Android довольны этой конструкцией. Полная реализация связана с видео или переключением на главную ветку в GitHub.

1

Решение

// что делать, чтобы поток завершился ??

Кажется, здесь правильно сделать join нить. Очевидно, что из-за того, что код в настоящее время структурирован, у вас нет доступа к thread объект.

void workerThread()
{
CurlData workerCurlData;
while (true)
{
....
}
}

Здесь, я думаю, вы хотели бы заменить жестко true с переменной (например, возможно, типа std::atomic<bool>) который можно инициализировать как true и будет установлен на false когда поток должен быть закрыт.

Функция, ответственная за закрытие потока, назначит false а затем позвоните join ждать, пока поток завершит свое выполнение.


Замечания:

  • Похоже, что есть звонок, который не охраняется mutex (Т.е. if (putStack.empty())в начале while петля в workerThread функция
  • Вы также можете посмотреть в std::lock_guard безопасно обрабатывать блокировку и разблокировку std::mutex
  • Возможно, вы захотите заменить жестко запрограммированный сон соответствующими переменными состояния (т.е. std::condition_variable перевести поток в спящий режим до тех пор, пока он не начнет снова работать (т.е. вместо используемого метода опроса)

Пример кода

#include <atomic>
#include <chrono>
#include <iostream>
#include <mutex>
#include <queue>
#include <thread>

std::mutex gMutex;
std::queue<int> gQueue;
std::atomic<bool> gRunThread(true);
std::thread gWorkerThread;

void workerThread();

void driver();

void start();

void addData(int i);

void end();

int main()
{
std::thread driverThread(&driver);
driverThread.join();

return 0;
}

void driver()
{
std::cout << "Starting ...\n";
std::this_thread::sleep_for(std::chrono::seconds(1));
start();

std::this_thread::sleep_for(std::chrono::seconds(1));
for (auto i = 0; i < 5; ++i)
{
std::this_thread::sleep_for(std::chrono::seconds(1));
addData(i);
}

std::cout << "Ending ...\n";
std::this_thread::sleep_for(std::chrono::seconds(1));
end();
}

void workerThread()
{
while (gRunThread)
{
bool isEmpty;

{
std::lock_guard<std::mutex> lock(gMutex);
isEmpty = gQueue.empty();
}

if (isEmpty)
{
std::cout << "Waiting for the queue to fill ...\n";
std::this_thread::sleep_for(std::chrono::seconds(2));
}
else
{
std::lock_guard<std::mutex> lock(gMutex);

int value = gQueue.front();
gQueue.pop();

std::cout << "Dequeued: " << value << "\n";
}
}
}

void start()
{
gWorkerThread = std::thread(&workerThread);
}

void addData(int i)
{
{
std::lock_guard<std::mutex> lock(gMutex);
gQueue.push(i);
}

std::cout << "Queued: " << i << "\n";
}

void end()
{
gRunThread = false;
gWorkerThread.join();
}

Пример вывода кода

Starting ...
Waiting for the queue to fill ...
Waiting for the queue to fill ...
Queued: 0
Queued: 1
Dequeued: 0
Dequeued: 1
Waiting for the queue to fill ...
Queued: 2
Queued: 3
Dequeued: 2
Dequeued: 3
Waiting for the queue to fill ...
Queued: 4
Ending ...

Живой пример


редактировать

Из комментариев я теперь понимаю, что ваш код находится в библиотеке, которая динамически загружается и выгружается. Выгрузка библиотеки приведет к уничтожению объекта глобального потока. Поэтому любые действия, которые будут выгружать библиотеку, должны обрабатываться, чтобы гарантировать правильное завершение потока.

0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]