Я искал в Интернете, но не могу найти хороший пример того, что я пытаюсь достичь. Используя c ++, я работаю с визуализацией для Kodi (приложение с открытым исходным кодом для медиацентра). Эта визуализация отправляет данные в виде http-клиента на мост Philips Hue, http-сервер, для изменения цветов и других атрибутов источников света в соответствии с музыкой. Я использую cURL для обработки запросов http.
Использование одного потока приводит к проблемам, при которых cURL требует времени для выполнения своей работы и получения ответов от моста Philips Hue (http-сервер). Это блокирует визуализацию на экране и иногда вывод звука. Вы можете увидеть задержки здесь: YouTube видео визуализации.
То, как надстройки структурированы в Kodi, означает отсутствие «основной» функции. Так что для многопоточности я не могу найти хорошую конструкцию, так как большинство примеров в Интернете создают поток в main и присоединяются к нему позже в main. На данный момент я пытаюсь что-то вроде этого:
Я пробовал несколько методов для создания потока, но при отладке в 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.
// что делать, чтобы поток завершился ??
Кажется, здесь правильно сделать 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 ...
Из комментариев я теперь понимаю, что ваш код находится в библиотеке, которая динамически загружается и выгружается. Выгрузка библиотеки приведет к уничтожению объекта глобального потока. Поэтому любые действия, которые будут выгружать библиотеку, должны обрабатываться, чтобы гарантировать правильное завершение потока.
Других решений пока нет …