I'm not sure if I developed everything correct.
At least it's working, but is it realy correct?
But sometimes I get an error when starting the service. If I try again it's starting.
Thanks for your help.
I developed a Service which creates a Thread "Importer".
The Importer read the configuration like connection params from the registry. Here I get sometimes the problem that some parameter could not read.
The Import check for files with extension .jpg in a directory. The path of the directory is also stored in registry.
If JPG are existing in directory they will be imported in a database and removed from filesystem.
All JPG are imported the Thread "Importer" will sleep for x minutes which is also configured in registry.
The service
//---------------------------------------------------------------------------
void __fastcall TSrvCrateImage::ServiceExecute(TService *Sender) {
while (!Terminated) {
ServiceThread->ProcessRequests(false);
// MyImporter->Resume();
Sleep(1000);
}
}
//---------------------------------------------------------------------------
void __fastcall TSrvCrateImage::ServiceStart(TService *Sender, bool &Started) {
MyImporter = new Importer(false);
bool valid = ReadConfig();
if (valid) {
Started = true;
} else {
Started = false;
}
}
//---------------------------------------------------------------------------
bool __fastcall TSrvCrateImage::ReadConfig() {
UnicodeString msg = MyImporter->ReadConfig();
if (!msg.IsEmpty()) {
LogMessage(msg, EVENTLOG_ERROR_TYPE);
return false;
}
LogMessage("Configuration loaded.", EVENTLOG_INFORMATION_TYPE);
return true;
}
//---------------------------------------------------------------------------
void __fastcall TSrvCrateImage::ServiceStop(TService *Sender, bool &Stopped) {
MyImporter->Terminate();
Stopped = true;
}
The Importer
__fastcall Importer::Importer(bool CreateSuspended)
: TThread(CreateSuspended) {
m_SleepMin = 0;
m_ConfFile = new TRegConfigFile();
}
//---------------------------------------------------------------------------
void __fastcall Importer::Execute() {
try {
while (!Terminated) {
ImportImageFiles();
Sleep(m_SleepMin * 60 * 1000);
}
} catch (Exception &exception) {
SrvCrateImage->LogMessage("Importer::Execute() " + exception.ToString(), EVENTLOG_ERROR_TYPE);
}
}
Thanks a lot
I started the service and sometimes I get errors that some paramters are not existing.
I don't know what happen if a problem will occure.
Will the service and thread still work? Or not due to bad thread programming...
I see a number of issues with your code.
get rid of the TService::OnExecute event handler completely. You don't need it. It is not doing anything useful. TService will handle SCM requests internally for you when there is no OnExecute handler assigned.
What is the point of creating a new thread for the importer if you are going to make the service thread dependent on reading the config from the importer? You have two threads fighting over a single config without any synchronization between them. I would suggest having the service thread read in the config before starting the importer thread.
Your service's OnStop event handler is signaling the importer thread to terminate, but is not waiting for the thread to fully terminate, let alone destroying the thread. It needs to do both.
With that said, try something more like this:
Service:
//---------------------------------------------------------------------------
void __fastcall TSrvCrateImage::ServiceStart(TService *Sender, bool &Started) {
TRegConfigFile *ConfFile = ReadConfig();
if (ConfFile) {
MyImporter = new Importer(false, ConfFile);
Started = true;
} else {
Started = false;
}
}
//---------------------------------------------------------------------------
TRegConfigFile* __fastcall TSrvCrateImage::ReadConfig() {
String msg;
// read config directly, not from importer thread...
TRegConfigFile *ConfFile = new TRegConfigFile();
try {
msg = ...; // read from ConfFile as needed?
}
catch (Exception &exception) {
msg = exception.ToString();
}
if (!msg.IsEmpty()) {
LogMessage(msg, EVENTLOG_ERROR_TYPE);
delete ConfFile;
return NULL;
}
LogMessage("Configuration loaded.", EVENTLOG_INFORMATION_TYPE);
return ConfFile;
}
//---------------------------------------------------------------------------
void __fastcall TSrvCrateImage::ServiceStop(TService *Sender, bool &Stopped) {
if (MyImporter) {
MyImporter->Terminate();
HANDLE h = (HANDLE) MyImporter->Handle;
while (WaitForSingleObject(h, WaitHint-100) == WAIT_TIMEOUT) {
ReportStatus();
}
delete MyImporter;
}
Stopped = true;
}
Importer:
__fastcall Importer::Importer(bool CreateSuspended, TRegConfigFile *ConfFile)
: TThread(CreateSuspended) {
m_ConfFile = ConfFile;
m_SleepMin = ...; // read from m_ConfFile as needed
...
}
//---------------------------------------------------------------------------
__fastcall Importer::~Importer() {
delete m_ConfFile;
}
//---------------------------------------------------------------------------
void __fastcall Importer::Execute() {
while (!Terminated) {
ImportImageFiles();
Sleep(m_SleepMin * 60 * 1000);
}
}
//---------------------------------------------------------------------------
void __fastcall Importer::DoTerminate() {
if (FatalException) {
SrvCrateImage->LogMessage("Importer " + ((Exception*)FatalException)->ToString(), EVENTLOG_ERROR_TYPE);
}
TThread::DoTerminate();
}
Related
I'm currently using TIdHTTP from Indy with my Embarcadero C++Builder 10.1 Berlin. I have read some online tutorials on how to make TIdHTTP multi-threaded, but the main issue is I already tested this component in a thread.
So here is how it works. I created a thread object and I made a function to download a file in that thread, apparently the thread works fine and the file get downloaded to my Disk. But, when I create additional threads for file downloads, the first thread stops. I don't want that, I want both files to continue to download at the same time (without pausing the first thread), like in IDM (Internet Download Manager).
The thread class is like in the code below:
class TIdHTTPThread : public TThread
{
protected:
void __fastcall Execute();
void __fastcall PutDownloadedFile();
public:
__fastcall TIdHTTPThread(bool CreateSuspended);
void __fastcall IdHTTPBeginWork(TObject *ASender, TWorkMode AWorkMode,
__int64 AWorkCountMax);
void __fastcall IdHTTPWork(TObject *ASender, TWorkMode AWorkMode,
__int64 AWorkCount);
void __fastcall IdHTTPEndWork(TObject *ASender, TWorkMode AWorkMode);
void __fastcall DownloadFile(UnicodeString AFileURL, UnicodeString AFileDest);
void __fastcall CreateQueue(TWinControl* wcParent, TAlign alAlign);
private:
TIdHTTP* IdHTTP;
TMemoryStream* msMemoryStream;
UnicodeString uFileURL;
UnicodeString uFileDest;
int iDownProgress;
int iFileSize;
int iMaxProgress;
int iDownSpeed;
TWinControl* wcParent;
TIFDQueue *ifdQueue;
};
Please don't bother about the additional properties and methods in the class, I just want to achieve what I need in my question.
CPP File:
void __fastcall TIdHTTPThread::CreateQueue(TWinControl* wcParent, TAlign alAlign)
{
this->wcParent = wcParent;
ifdQueue = new TIFDQueue(this->wcParent, alAlign);
}
void __fastcall TIdHTTPThread::IdHTTPBeginWork(TObject *ASender, TWorkMode AWorkMode,
__int64 AWorkCountMax)
{
this->iFileSize = AWorkCountMax;
this->iMaxProgress = AWorkCountMax;
ifdQueue->SetFileSize(this->iFileSize);
ifdQueue->SetMaxProgress(this->iMaxProgress);
ifdQueue->SetFileURL(this->uFileURL);
ifdQueue->SetFilePath(this->uFileDest);
ifdQueue->OnBeginUpdate();
}
void __fastcall TIdHTTPThread::IdHTTPWork(TObject *ASender, TWorkMode AWorkMode,
__int64 AWorkCount)
{
this->iDownProgress = AWorkCount;
this->iDownSpeed = AWorkCount / 1024;
ifdQueue->SetDownProgress(this->iDownProgress);
ifdQueue->SetDownSpeed(this->iDownSpeed);
ifdQueue->OnWorkUpdate();
}
void __fastcall TIdHTTPThread::IdHTTPEndWork(TObject *ASender, TWorkMode AWorkMode)
{
ifdQueue->OnEndUpdate();
this->Terminate();
}
//**//
void __fastcall TIdHTTPThread::DownloadFile(UnicodeString AFileURL, UnicodeString AFileDest)
{
this->uFileURL = AFileURL;
this->uFileDest = AFileDest;
}
void __fastcall TIdHTTPThread::PutDownloadedFile()
{
try {
this->msMemoryStream = new TMemoryStream;
this->IdHTTP = new TIdHTTP(NULL);
this->IdHTTP->OnWorkBegin = this->IdHTTPBeginWork;
this->IdHTTP->OnWork = this->IdHTTPWork;
this->IdHTTP->OnWorkEnd = this->IdHTTPEndWork;
this->IdHTTP->ConnectTimeout = 20000;
this->IdHTTP->ReadTimeout = 60000;
this->IdHTTP->Get(this->uFileURL, this->msMemoryStream);
this->msMemoryStream->SaveToFile(this->uFileDest);
} __finally {
delete this->msMemoryStream;
delete this->IdHTTP;
}
}
__fastcall TIdHTTPThread::TIdHTTPThread(bool CreateSuspended)
: TThread(CreateSuspended)
{
}
//---------------------------------------------------------------------------
void __fastcall TIdHTTPThread::Execute()
{
//---- Place thread code here ----
FreeOnTerminate = true;
Synchronize(&this->PutDownloadedFile);
}
//---------------------------------------------------------------------------
UPDATE:
void __fastcall TIdHTTPThread::PutDownloadedFile()
{
try {
this->CookieManager = new TIdCookieManager(NULL);
this->SSLIOHandlerSocket = new TIdSSLIOHandlerSocketOpenSSL(NULL);
this->msMemoryStream = new TMemoryStream;
// Configure SSL IOHandler
this->SSLIOHandlerSocket->SSLOptions->Method = sslvSSLv23;
this->SSLIOHandlerSocket->SSLOptions->SSLVersions = TIdSSLVersions() << sslvTLSv1_2 << sslvTLSv1_1 << sslvTLSv1;
// Setup HTTP
this->IdHTTP = new TIdHTTP(NULL);
this->ifdQueue->StopDownload(this->IdHTTP); // Function To stop download When Fired (doesn't fire imidiatly)
this->IdHTTP->OnWorkBegin = this->IdHTTPBeginWork;
this->IdHTTP->OnWork = this->IdHTTPWork;
this->IdHTTP->OnWorkEnd = this->IdHTTPEndWork;
this->IdHTTP->OnRedirect = this->IdHTTPRedirect;
this->IdHTTP->HandleRedirects = true;
this->IdHTTP->AllowCookies = true;
this->IdHTTP->CookieManager = this->CookieManager;
this->IdHTTP->IOHandler = this->SSLIOHandlerSocket;
this->IdHTTP->Get(this->uFileURL, this->msMemoryStream);
if ( this->msMemoryStream->Size >= this->IdHTTP->Response->ContentLength ) {
this->msMemoryStream->SaveToFile(this->uFileName);
}
} __finally {
delete this->msMemoryStream;
delete this->CookieManager;
delete this->SSLIOHandlerSocket;
delete this->IdHTTP;
}
}
The problem is your thread's Execute() method is doing ALL of its work inside a Synchronize() call, so ALL of its work is actually being done inside the main UI thread instead, thus serializing your downloads and defeating the whole point of using a worker thread.
DO NOT call PutDownloadedFile() itself with Synchronize(). You need to instead change your individual TIdHTTP status events to use Synchronize() (or Queue()) when updating your UI controls.
I just merely want to emit 2 signal and a Qtimer timeout on button click from a GUI.
Although 2 SIGNAL/SLOT WORKS the QTimer's SIGNAL/SLOT is not working, the whentimeout SLOT never works.
There is no error even.
GUI(DIALOG.CPP)
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
fduthread = new fdustatus(this);
connect(fduthread,SIGNAL(NumberChanged(int)),this,SLOT(onNumberChanged(int)));
connect(fduthread,SIGNAL(nameChange(QString)),this,SLOT(onNameChanged(QString)));
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::onNumberChanged(int number)
{
ui->label->setText(QString::number(number));
}
void Dialog::onNameChanged(QString s)
{
ui->label_2->setText(s);
}
void Dialog::on_pushButton_clicked()
{
fduthread->start();
fduthread->stop=false;
}
void Dialog::on_pushButton_2_clicked()
{
ui->label_2->setText("");
fduthread->stop=true;
}
THAT IS MY THREAD
#include "fdustatus.h"
#include<QMutex>
#include<QTimer>
#include<QDebug>
fdustatus::fdustatus(QObject *parent):QThread(parent)
{
}
void fdustatus::run()
{
mytimer = new QTimer();
mytimer->setInterval(10);
connect( mytimer,SIGNAL(timeout()),this,SLOT(whentimeout()));
mytimer->start();
for(int i =0;i<100;i++)
{
QMutex mutex;
mutex.lock();
if(this->stop)break;
mutex.unlock();
emit NumberChanged(i*10);
emit nameChange(getstring());
this->msleep(100);
}
}
QString fdustatus::getstring()
{
QString networkport;
networkport.append("Alarm Active");
return networkport;
}
void fdustatus::whentimeout()
{
qDebug() << "timer ended from thread..";
}
I want to serialize network requests using QNetworkAccessManager. For achieving it i wrote such class:
#ifndef CLIENT_H
#define CLIENT_H
#include <queue>
#include <mutex>
#include <condition_variable>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QNetworkRequest>
class Client : public QObject
{
Q_OBJECT
struct RequestRecord
{
RequestRecord(QString u, int o):url(u),operation(o){}
QString url;
int operation;
};
std::mutex mutex;
std::queue<RequestRecord*> requests;
QNetworkAccessManager *manager;
bool running;
std::condition_variable cv;
public:
Client():manager(nullptr){}
~Client()
{
if(manager)
delete manager;
}
void request_cppreference()
{
std::unique_lock<std::mutex> lock(mutex);
requests.push(new RequestRecord("http://en.cppreference.com",0));
cv.notify_one();
}
void request_qt()
{
std::unique_lock<std::mutex> lock(mutex);
requests.push(new RequestRecord("http://doc.qt.io/qt-5/qnetworkaccessmanager.html",1));
cv.notify_one();
}
void process()
{
manager = new QNetworkAccessManager;
connect(manager,&QNetworkAccessManager::finished,[this](QNetworkReply *reply)
{
std::unique_lock<std::mutex> lock(mutex);
RequestRecord *front = requests.front();
requests.pop();
delete front;
reply->deleteLater();
});
running = true;
while (running)
{
std::unique_lock<std::mutex> lock(mutex);
cv.wait(lock);
RequestRecord *front = requests.front();
manager->get(QNetworkRequest(QUrl(front->url)));
}
}
};
#endif // CLIENT_H
As one can see, there are 2 methods for requesting data from network and method process, which should be called in separate thread.
I'm using this class as follows:
Client *client = new Client;
std::thread thr([client](){
client->process();
});
std::this_thread::sleep_for(std::chrono::seconds(1));
client->request_qt();
std::this_thread::sleep_for(std::chrono::milliseconds(1));
client->request_cppreference();
This example illustrate 2 consecutive requests to network from one thread and processing of these request in another. All works fine except my lambda is never called. Requests are sent (checked it using wireshark), but i cannot get replies. What is the cause?
as #thuga suppose the problem was in event loop. My thread always waiting on cv and thus cannot process events, little hack solve the problem:
void process()
{
manager = new QNetworkAccessManager;
connect(manager,&QNetworkAccessManager::finished,[this](QNetworkReply *reply)
{
std::unique_lock<std::mutex> lock(mutex);
RequestRecord *front = requests.front();
requests.pop();
delete front;
qDebug() << reply->readAll();
processed = true;
reply->deleteLater();
});
running = true;
while (running)
{
{
std::unique_lock<std::mutex> lock(mutex);
cv.wait(lock);
if(requests.size() > 0 && processed)
{
RequestRecord *front = requests.front();
manager->get(QNetworkRequest(QUrl(front->url)));
processed = false;
QtConcurrent::run([this]()
{
while (running)
{
cv.notify_one();
msleep(10);
}
});
}
}
QCoreApplication::processEvents();
}
}
};
it's not beautiful obvious since it is using 3 threads instead of 2, but it is Qt with this perfect phrase:
QUrl QNetworkReply::url() const Returns the URL of the content
downloaded or uploaded. Note that the URL may be different from that
of the original request. If the
QNetworkRequest::FollowRedirectsAttribute was set in the request, then
this function returns the current url that the network API is
accessing, i.e the url emitted in the QNetworkReply::redirected
signal.
I have a reusable class that starts up an infinite thread. this thread can only be killed by calling a stop function that sets a kill switch variable. When looking around, there is quite a bit of argument over volatile vs atomic variables.
The following is my code:
program.cpp
int main()
{
ThreadClass threadClass;
threadClass.Start();
Sleep(1000);
threadClass.Stop();
Sleep(50);
threaClass.Stop();
}
ThreadClass.h
#pragma once
#include <atomic>
#include <thread>
class::ThreadClass
{
public:
ThreadClass(void);
~ThreadClass(void);
void Start();
void Stop();
private:
void myThread();
std::atomic<bool> runThread;
std::thread theThread;
};
ThreadClass.cpp
#include "ThreadClass.h"
ThreadClass::ThreadClass(void)
{
runThread = false;
}
ThreadClass::~ThreadClass(void)
{
}
void ThreadClass::Start()
{
runThread = true;
the_thread = std::thread(&mythread, this);
}
void ThreadClass::Stop()
{
if(runThread)
{
runThread = false;
if (the_thread.joinable())
{
the_thread.join();
}
}
}
void ThreadClass::mythread()
{
while(runThread)
{
//dostuff
Sleep(100); //or chrono
}
}
The code that i am representing here mirrors an issue that our legacy code had in place. We call the stop function 2 times, which will try to join the thread 2 times. This results in an invalid handle exception. I have coded the Stop() function in order to work around that issue, but my question is why would the the join fail the second time if the thread has completed and joined? Is there a better way programmatically to assume that the thread is valid before trying to join?
I want to remove some Windows dependencies in how the threading is done in an old code, how can I convert this piece of code to C++11 threading style?
MyClass run Method:
void MyClass::run()
{
while(true)
{
WaitForSingleObject(startEvent, INFINITE);
processData();
ResetEvent(startEvent);
SetEvent(hEvent);
}
}
Main Update in another class:
{
.
.
.
WaitForSingleObject(myClassInstance.hEvent, INFINITE);
ResetEvent(myClassInstance.hEvent);
// Getting data processed by myClassInstance in the previous update call
// Mem copies to myClassInstance to be used later by myClassInstance processData()
SetEvent(myClassInstance.startEvent);
.
.
.
}
You can create an event class easily with std::condition_variable and a bool:
class Event {
std::condition_variable cv_;
std::mutex mtx_;
bool signaled_ = false;
public:
void wait() {
std::unique_lock<std::mutex> lock{mtx_};
while (!signaled_) {
cv_.wait(lock);
}
}
void reset() {
std::lock_guard<std::mutex> lock{mtx_};
signaled_ = false;
}
void set() {
{
std::lock_guard<std::mutex> lock{mtx_};
signaled_ = true;
}
cv_.notify_one();
}
};
Resulting in the usage:
struct MyClass {
Event start;
Event ready;
void processData();
void run();
};
void MyClass::run() {
while (true) {
start.wait();
processData();
start.reset();
ready.set();
}
}
void main_update_in_another_class() {
ready.wait();
ready.reset();
// Getting data processed by myClassInstance in the previous update call
// Mem copies to myClassInstance to be used later by myClassInstance processData()
start.set();
// Do other things that don't require access to myClassInstance
}
See the live demo at Coliru.