[C++, windows form]How do I make the main thread wait for the called thread to finish? - multithreading

I created 2 buttons, one for start a new thread, the other to end it. The actual calculation inside the new thread involved new[] and delete[] so I don't abort the thread directly, but using a flag to end it. It may take some time to end the delete[] and result-saving, so I want the main thread to wait for the new thread to end. But however I tried, I find the new thread doesn't run(though its ThreadState is running) until the command lines for the stop-button are conducted. System::Threading::Thread works quite different from thread to me. Is it how it should be?
#include "stdafx.h"
ref class Form1 : System::Windows::Forms::Form
{
public:
//define a thread name and a flag to terminate the thread
System::Threading::Thread^ th1;
static bool ITP1=0;
Form1(void)
{InitializeComponent();}
System::Windows::Forms::Button^ ButtonStart;
System::Windows::Forms::Button^ ButtonStop;
System::Windows::Forms::Label^ Label1;
void InitializeComponent(void)
{
this->SuspendLayout();
this->ButtonStart = gcnew System::Windows::Forms::Button();
this->ButtonStart->Location = System::Drawing::Point(20, 20);
this->ButtonStart->Click += gcnew System::EventHandler(this, &Form1::ButtonStart_Click);
this->Controls->Add(this->ButtonStart);
this->ButtonStop = gcnew System::Windows::Forms::Button();
this->ButtonStop->Location = System::Drawing::Point(120, 20);
this->ButtonStop->Click += gcnew System::EventHandler(this, &Form1::ButtonStop_Click);
this->Controls->Add(this->ButtonStop);
this->Label1 = gcnew System::Windows::Forms::Label();
this->Label1->Location = System::Drawing::Point(20, 80);
this->Controls->Add(this->Label1);
this->ResumeLayout(false);
}
void ThreadStart()
{
for (int idx=0;idx<999999999;++idx)
{
if (ITP1) break;
}
this->Label1->Text = "finished";
ITP1=0;
}
System::Void ButtonStart_Click(System::Object^ sender, System::EventArgs^ e)
{
th1 = gcnew System::Threading::Thread(gcnew System::Threading::ThreadStart(this,&Form1::ThreadStart));
th1->Start();
this->Label1->Text = "running";
}
System::Void ButtonStop_Click(System::Object^ sender, System::EventArgs^ e)
{
if (th1->ThreadState==System::Threading::ThreadState::Running)
{
//use the flag to stop the thread
ITP1=1;
//the wait method using while+sleep doesn't work
while (th1->ThreadState==System::Threading::ThreadState::Running) System::Threading::Thread::Sleep(1000);
//replacing the wait method above with "th1->Join()" doesn't work either
}
}
};
int main()
{
Form1^ A1 = gcnew Form1();
A1->ShowDialog();
return 0;
}

You have to join() the called thread in the main thread. Then the main thread will wait until the called thread is finished.
See the documentation for Join to know how it is to be called.

Finally I found the cause. It's just the "this->" pointer in the new thread. Removing it makes everything OK.
I suppose it's because the Form allows operation on only one element at the same time. I ask the button-click to wait for the new thread, and the new thread tries to edit the other form element. They wait for each other to end and cause a dead loop.

Related

Creating new thread causing exception

I have a timer that will create a new thread and wait for the timer to expire before calling the notify function. It works correctly during the first execution, but when the timer is started a second time, an exception is thrown trying to create the new thread. The debug output shows that the previous thread has exited before attempting to create the new thread.
Timer.hpp:
class TestTimer
{
private:
std::atomic<bool> active;
int timer_duration;
std::thread thread;
std::mutex mtx;
std::condition_variable cv;
void timer_func();
public:
TestTimer() : active(false) {};
~TestTimer() {
Stop();
}
TestTimer(const TestTimer&) = delete; /* Remove the copy constructor */
TestTimer(TestTimer&&) = delete; /* Remove the move constructor */
TestTimer& operator=(const TestTimer&) & = delete; /* Remove the copy assignment operator */
TestTimer& operator=(TestTimer&&) & = delete; /* Remove the move assignment operator */
bool IsActive();
void StartOnce(int TimerDurationInMS);
void Stop();
virtual void Notify() = 0;
};
Timer.cpp:
void TestTimer::timer_func()
{
auto expire_time = std::chrono::steady_clock::now() + std::chrono::milliseconds(timer_duration);
std::unique_lock<std::mutex> lock{ mtx };
while (active.load())
{
if (cv.wait_until(lock, expire_time) == std::cv_status::timeout)
{
lock.unlock();
Notify();
Stop();
lock.lock();
}
}
}
bool TestTimer::IsActive()
{
return active.load();
}
void TestTimer::StartOnce(int TimerDurationInMS)
{
if (!active.load())
{
if (thread.joinable())
{
thread.join();
}
timer_duration = TimerDurationInMS;
active.store(true);
thread = std::thread(&TestTimer::timer_func, this);
}
else
{
Stop();
StartOnce(TimerDurationInMS);
}
}
void TestTimer::Stop()
{
if (active.load())
{
std::lock_guard<std::mutex> _{ mtx };
active.store(false);
cv.notify_one();
}
}
The error is being thrown from my code block here:
thread = std::thread(&TestTimer::timer_func, this);
during the second execution.
Specifically, the error is being thrown from the move_thread function: _Thr = _Other._Thr;
thread& _Move_thread(thread& _Other)
{ // move from _Other
if (joinable())
_XSTD terminate();
_Thr = _Other._Thr;
_Thr_set_null(_Other._Thr);
return (*this);
}
_Thrd_t _Thr;
};
And this is the exception: Unhandled exception at 0x76ED550B (ucrtbase.dll) in Sandbox.exe: Fatal program exit requested.
Stack trace:
thread::move_thread(std::thread &_Other)
thread::operator=(std::thread &&_Other)
TestTimer::StartOnce(int TimerDurationInMS)
If it's just a test
Make sure the thread handler is empty or joined when calling the destructor.
Make everything that can be accessed from multiple threads thread safe (specifically, reading the active flag). Simply making it an std::atomic_flag should do.
It does seem like you are killing a thread handle pointing to a live thread, but hard to say without seeing the whole application.
If not a test
...then generally, when need a single timer, recurreing or not, you can just go away with scheduling an alarm() signal into itself. You remain perfectly single threaded and don't even need to link with the pthread library. Example here.
And when expecting to need more timers and stay up for a bit it is worth to drop an instance of boost::asio::io_service (or asio::io_service if you need a boost-free header-only version) into your application which has mature production-ready timers support. Example here.
You create the TestTimer and run it the first time via TestTimer::StartOnce, where you create a thread (at the line, which later throws the exception). When the thread finishes, it sets active = false; in timer_func.
Then you call TestTimer::StartOnce a second time. As active == false, Stop() is not called on the current thread, and you proceed to creating a new thread in thread = std::thread(&TestTimer::timer_func, this);.
And then comes the big but:
You have not joined the first thread before creating the second one. And that's why it throws an exception.

Qt thread ID is equal to MainWindows? (moveToThread)

Now I want to create a thread and put in my class "AVC_file" inatance.
But when I print currentThreadId in textBroswer, I found MainWindows's threadID is same with the thread I created. show pitures like below.
Framework::Framework(QWidget * parent) : QMainWindow(parent)
{
ui.setupUi(this);
int threadID = (int)QThread::currentThreadId();
ui.textBrowser->append("Main Tread ID : " + QString::number(threadID));
}
void Framework::on_OpenAVCFile_clicked()
{
QString filePath = QFileDialog::getOpenFileName(
this, tr("Open File"), "C:\\", "AVC File (*.avc)"
);
if (!filePath.isEmpty())
{
QMessageBox::information(this, tr("File Name"), filePath);
}
QFile file(filePath);
if (!file.open(QIODevice::ReadOnly))
{
QMessageBox::information(0, "info", file.errorString());
}
else {
QThread *thread = new QThread(this);
int threadID = (int)thread->currentThreadId();
ui.textBrowser->append("Second Tread ID : " + QString::number(threadID) + "\n");
AVC_File *AVC_file = new AVC_File();
AVC_file->moveToThread(thread);
connect(AVC_file, SIGNAL(requestFileContent(QString)), this, SLOT(addFileContent(QString)));
connect(AVC_file, SIGNAL(requestFileDebug(QString)), this, SLOT(addFileDebug(QString)));
connect(AVC_file, SIGNAL(requestFileCorrectness(bool, int)), this, SLOT(adddFileCorrectness(bool, int)));
connect(AVC_file, SIGNAL(requestNewValue(unsigned int, int)), this, SLOT(addNewValue(unsigned int, int)));
thread->start();
AVC_file->AVC_FileCheck(file);
}
}
Images about my code and results-->
Main Windows, create thread and results
Oh!I also try emit info in my "AVC_file" instance?like below.
void AVC_File::AVC_FileCheck(QFile &file)
{
int threadID = (int)QThread::currentThreadId();
emit requestFileContent("Thread ID by emit" + QString::number(threadID) + "\n");
QTextStream in(&file);
........
........
}
Emit threadID info
Anyone can help me?
BTW, I use visual studio Qt add-in to develop this project.
QThread::currentThreadId() is a static method.
When you call it, it returns the thread ID of the thread that executes it.
In both your cases that's the main thread.
There are several issues that I'll address in random order.
First of all, using thread IDs is bad user experience. Give the threads a descriptive name:
int main(...) {
QApplication app(...);
QThread myThread;
MyObject myObject;
myObject->moveToThread(&myThread);
QThread::currentThread()->setObjectName("mainThread");
myThread.setObjectName("myThread");
...
}
Then use QThread::currentThread()->objectName() to retrieve it. You can also pass QObject* to qDebug() to display the name of the thread:
qDebug() << QThread::currentThread();
Your signal invocation would then become:
QString currentThreadName() {
return QThread::currentThread()->objectName().isEmpty() ?
QStringLiteral("0x%1").arg(QThread::currentThread(), 0, 16) :
QThread::currentThread()->objectName();
}
...
emit requestFileContent(
QStringLiteral("Emitting from thread \"%1\"\n").arg(currentThreadName));
Then, use the above to deal with the thread you've created:
auto thread = new QThread(this);
thread->setObjectName("fileThread");
ui.textBrowser->append(QStringLiteral("Worker thread: \"%1\").arg(thread->objectName()));
auto AVC_file = new AVC_File;
AVC_file->moveToThread(thread);
...
But AVC_FileCheck is invoked from the main thread. Whether that's OK or not depends on how that method is implemented. It needs to be thread-safe, see this question for a discussion of that. TL;DR: The following pattern could be a starting point:
class AVC_file : public QObject {
Q_OBJECT
Q_SLOT void fileCheck_impl(QIODevice * dev) {
dev->setParent(this);
...
}
Q_SIGNAL void fileCheck_signal(QIODevice *);
public:
void fileCheck(QIODevice *dev) { fileCheck_signal(dev); }
AVC_file(QObject *parent = nullptr) : QObject(parent) {
connect(this, &AVC_file::fileCheck_signal, this, &AVC_file::fileCheck_impl);
...
}
};
Finally, your existing AVC_fileCheck API is broken. You pass QFile by reference: this won't ever work since it ceases to exist as soon as on_OpenAVCFile_clicked returns. When AVC_file uses that file in its thread, it's a dangling object reference.
Instead, you must pass the ownership of the file to AVC_file, and pass a pointer to an instance that AVC_file will dispose when done with. Or simply let AVC_file open the file for you!

Invoke Windows Form Thread and Sleeping

I have a managed c++ application that I start a new thread to do some stuff and update some text boxes, it loops and sleeps at the end of every loop. Because of it sleeping I needed to have it in a new thread so the UI doesn't crash. Then I realized I need to invoke the thread that created the UI to access the textboxes, but now I'm back in the main thread so the sleeping crashes it. How should I approach this.
private: System::Void buttonStartCamera_Click(System::Object^ sender, System::EventArgs^ e)
{
ThreadStart^ threadStart = gcnew ThreadStart(this, &UserInterface::SetText);
Thread^ newThread = gcnew Thread(threadStart);
newThread->Start();
}
void SetText()
{
if (this->textBoxCameraOneX->InvokeRequired)
{
MyDel^ del = gcnew MyDel(this, &UserInterface::SetText);
this->Invoke(del);
}
else
{
int count = 0;
srand (time(NULL));
for (count = 0; count < 20; ++count)
{
for each (Camera^ camera in cameraList)
{
textBoxCameraOneX->Text = count.ToString();
}
Sleep(300);
}
}
}
The best option is likely to refactor this so your Sleep doesn't occur within the SetText method. Your background thread could use a separate method that performs the sleep, and then invokes the proper method to set the text (for one text box at a time) in a loop.
In general, you should keep the methods you use with Control::Invoke as short as possible - they should only include the logic required for your UI work, and not the other functionality.
That being said, in this case, it seems like a System::Windows::Forms::Timer would be more appropriate. You could set the interval to 300, and update a text box one at a time in the timer's Tick event.

How to Kill a waiting pthread

Have a pthread that sleeps while waiting on a condition variable. I use a boolean in the outer while loop to keep it running. The problem I seem to have is when I change this variable the thread does not die.
I took a look in instruments and if I start a thread , tell it to die, then start a new one my thread count is 2 not 1.
How can I properly destroy this thread when I want to?
int worktodo=0;
BOOL runthread=NO;
pthread_cond_t cond=PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
void *threadfunc(void *parm)
{
int rc;
while(runthread==YES)
{
rc=pthread_mutex_lock(&mutex);
while(!worktodo)
{
printf("thtread blocked\n");
rc=pthread_cond_wait(&cond, &mutex);
}
printf("thtread awake.... doing work\n");
// doing work
worktodo=0;
rc=pthread_mutex_unlock(&mutex);
}
// never reaches here!!
pthread_detach(NULL);
}
void makeThread()
{
pthread_attr_t attr;
int returnVal;
returnVal = pthread_attr_init(&attr);
assert(!returnVal);
runthread=YES;
returnVal = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
assert(!returnVal);
int threadError = pthread_create(&str->thread, &attr, &threadfunc, NULL);
returnVal = pthread_attr_destroy(&attr);
assert(!returnVal);
if (threadError != 0)
{
// Report an error.
}
}
void wakethread()
{
pthread_mutex_lock(&mutex);
worktodo=1;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
void killthread
{
runthread=NO;
}
thiton was correct. I couldnt kill the thread while it was blocked. Theres probably a better way to do this but the solution that worked for me was to set runthread to false then wake the thread.
void killthread
{
runthread=NO;
pthread_mutex_lock(&mutex);
worktodo=1;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
You initialize runthread to NO and compare it to YES. The thread should never reach the inside of its
while(runthread==YES)
loop. Besides, when the thread waits for work, killthread will not wake it up and runthread will stay in its work-waiting loop.

Marquee in seperate thread in c++ builder 10

I have just found that making marquee in the same thread it's text get stopped a little in a time when my application loads data..
I am asking if anybody has done marquee functionality in their application in a seperate thread using TTimer.
Even in you do the marquee in a thread, you still have to synchronize it with the main thread for display, so you will still have the same problem if you continue doing lengthy data loads in the main thread. So do the data loading in a separate thread instead, and leave the marquee (and all other UI elements and logic) in the main thread, where it belongs. You should not be doing blocking operations in the main thread to begin with.
HANDLE hThread;
DWORD ThreadId;
int Data_Of_Thread_1 = 1;
unsigned long __stdcall ThreadFunc(void *Arg)
{
int a=0;
while(a != 100000000000000000)
{
a++;
Form1->ListBox1->Items->Add(a);
}
return 0;
}
void __fastcall TForm1::Button1Click(TObject *Sender)
{
// hThread=CreateThread(NULL,0,ThreadFunc,0,0,&ThreadId);
hThread = CreateThread( NULL, 0, ThreadFunc, &Data_Of_Thread_1, 0, &ThreadId);
if ( hThread == NULL)
{
ExitProcess(Data_Of_Thread_1);
}
}
void __fastcall TForm1::Button2Click(TObject *Sender)
{
TerminateThread(hThread,ThreadId);
}

Resources