My thread closes while the program is running - multithreading

I'm programming a gmae in c++.net, it's like the space impact. I start the game form and on its load event I create a thread to play a music in the background of the application.
The problem is that when I press the space bar to shoot it also plays a sound, and it stops the main music.
I've already had this issue, but I was told that threads should fix the problem.
I've created the thread and my problem stands. Some help would be appreciated.
private: System::Void GameForm_Load(System::Object^ sender, System::EventArgs^ e)
{
Thread^ newThread = gcnew Thread(gcnew ThreadStart(PlayMusic));
newThread->Name = "MusicaImperio";
newThread->IsBackground = true;
if(newThread->IsBackground == true)
newThread->Start();
//Sleep(3000);
}
This is the load event, and this is the Playmusic method:
static void PlayMusic()
{
PlaySound(TEXT("../Musicas/Game.wav"), NULL, SND_ASYNC | SND_LOOP);
}

if you try to not run it on the background.
Does it work then or still nothing ?
maybe you can see what happens

Related

Use threads in a ref class form c++ to make parallel works

I am currently working in an HMI. I have to send a message and wait for a response. But if i use a while loop to wait for the response, my HMI stops the interactions waiting for the while loop's end.
I tried to use threads, and I tried to make a threads function in the HMI ref class to execute the function which contains the while loop. But I don't know how to do it. can you help me with that, or give me another way to make parallel work? Thanks
This is the code which contains the while loop from another class
bool Robot::waitArrival(char* at)
{
char _status[256];
strcpy(_status, "Going to ");
strcat(_status, static_cast<char*>(at));
do
{
this->handUpdates->lock();
strcpy(this->status, this->handUpdates->getStatus());
this->handUpdates->unlock();
ArUtil::sleep(1000); //1s
} while (strcmp(this->status, _status) == 0);
strcpy(_status, "Arrived at ");
strcat(_status, static_cast<char*>(at));
if (strcmp(this->status, _status) == 0)
return true;
else
return false;
}
and this is my HMI. By clicking button3 I call the ctr_main() function in the Controller ref class, the ctr_main() function calls the waitArrival() function,
private: System::Void button3_Click(System::Object^ sender, System::EventArgs^ e) {
Thread t1(&Controleur::ctr_main);
}

Multithreaded logger crashes after a while and I cannot find how to solve the problem

Intro
I am working on a multithreaded cross platform logging tool which purposes logging to the console and/or a file. However the problem I have right now is only related to the console logging.
Problem breakdown
The way the logger works is by adding a string to a Queue of strings. In the "LogToConsole" thread there is a wait method that waits until there is a string added to the queue. When that happens it should get notified and print, then pop and unlock.
Used variables
class Logger
{
public:
friend void LogToConsole(Logger* logger);
private:
std::atomic_flag _ThreadIsRunning { ATOMIC_FLAG_INIT };
std::thread _LogThread;
std::queue<std::string> _LogBuffer;
std::map<std::thread::id, std::string> _ThreadName;
std::mutex _LogMutex;
std::mutex _AppendLock;
std::condition_variable _LogLock;
...
The place I add data to the buffer
template<LogSeverity severity>
void Logger::Log(std::stringstream& message)
{
std::stringstream log;
log << _ThreadName[std::this_thread::get_id()] << ":\t";
log << message.str();
std::unique_lock<std::mutex> logAppendLock(_AppendLock);
_LogBuffer.push(log.str());
logAppendLock.unlock();
_LogLock.notify_one();
}
template<LogSeverity severity>
void Logger::Log(std::string message)
{
std::stringstream log;
log << message.c_str();
this->Log<severity>(log);
}
The thread that runs in a separate loop (notice however that this method is not part of the logger class):
void LogToConsole(Logger* logger)
{
do
{
std::unique_lock<std::mutex> lock(logger->_LogMutex);
logger->_LogLock.wait(lock);
std::printf("%s", logger->_LogBuffer.back().c_str());
logger->_LogBuffer.pop();
lock.unlock();
} while (logger->_ThreadIsRunning.test_and_set() || !logger->_LogBuffer.empty());
}
Place of thread creation
Logger::Logger() : _LogThread(), _LogBuffer(), _ThreadName(), _LogMutex()
{
_ThreadIsRunning.test_and_set();
_LogThread = std::thread(LogToConsole, this);
}
Test casing
std::shared_ptr<Logger> engineLogger = std::make_shared<Logger>();
engineLogger->SetThreadName("EngineLogger");
std::shared_ptr<Logger> coreLogger = std::make_shared<Logger>();
coreLogger->SetThreadName("CoreLogger");
while(true)
{
engineLogger->Log<LOG_INFO>("LOG\n");
coreLogger->Log<LOG_WARNING>("WARNING\n");
}
The code seems to be working threadsafe, no dataraces etc. but it crashes after a 5-10~ seconds. I have searched if there are people that are having a similar problem, this does not seem to be the case.
I am however not very experienced with concurrent programming, thus do not now how to handle these kind of problems very easily.
Hope someone can solve the problem or give me some advice to prevent the problem.
In while (logger->_ThreadIsRunning.test_and_set() || !logger->_LogBuffer.empty()); you access logger without a mutex, so that is a data race. Also _LogBuffer.push(log.str()); is accessed with only _AppendLock locked and logger->_LogBuffer.pop() is accessed with only _LogMutex locked, so that is another data race.
Data races are UB and a possible reason for the crash.

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

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.

WinForm Disappears in Multithreaded C++/CLI Application

I'm currently working on an application that needs to utilize a form to block the user from using the main form for a few seconds. Although running the code on the main thread seems to be okay, the labels on the pop up form don't render for about a second when the form first appears. I thought that if I ran that form on a separate thread, the rendering would be a lot smoother. The rendering is now so smooth that the form disappears immediately after rendering. The timer is set for five seconds, with a label on the screen counting down. Here's the relevant code calling the new thread and form:
System::Void MainScreen::runGame(int playerTurn) {
Thread ^ t = gcnew Thread(gcnew ThreadStart(gcnew MainScreen(),
&MainScreen::showModalDialog));
t->Start();
t->Join();
InitializeDice();
startTimer();
}
System::Void MainScreen::showModalDialog() {
GetReady ^ gr = gcnew GetReady();
gr->showModalPopup();
}
And here's the code inside the form:
public:
GetReady(void)
{
InitializeComponent();
}
System::Void showModalPopup() {
this->Show();
startTimer();
}
private: System::Void timerPrep_Tick(System::Object^ sender, System::EventArgs^ e) {
ts = ts->Subtract(TimeSpan(0, 0, 1));
if (ts->TotalSeconds <= 0) {
finishTimer();
} else {
lblTimer->Text = ts->ToString("mm\\:ss");
}
}
System::Void startTimer() {
array<String ^>^ minSec = gcnew array<String ^>(2);
minSec = lblTimer->Text->Split(':');
ts = gcnew TimeSpan(0, Convert::ToInt32(minSec[0]), Convert::ToInt32(minSec[1]));
Thread::Sleep(900);
timerPrep->Start();
}
System::Void finishTimer() {
timerPrep->Stop();
lblTimer->Text = "GO!!!";
Thread::Sleep(900);
this->Close();
}
My ideal solution would be to use a thread to generate the new form, so that rendering in both the main form and the pop up form is smooth.
Things I've tried:
Moving this->Show() every where I can think to put it.
I've added t->Join() to see if the main thread was trying to bring the main window back into focus. Thread t still executes and the timer still runs appropriately with the Join, blocking user input for five seconds - but there is nothing to block the screen.
I've read a few questions on SO, I think the most relevant one is WinForms multithreading issue - although I feel like that might be overkill for this situation.
If you have any ideas on what I need to do to have smooth rendering for both forms, please let me know. Thanks!
Displaying UI on a worker thread is filled with traps and surprises. The chief problem you are having here is that the thread doesn't pump a message loop. Required first of all to ensure that the window can receive Windows messages and to ensure that the thread doesn't immediately terminate. Beyond Jairo's recommendation to use ShowDialog(), the boilerplate solution is:
System::Void MainScreen::showModalDialog() {
Application::Run(gcnew GetReady);
}
There's more, a thread that displays windows should always be an STA thread. An implementation detail for COM and required to get stuff like the clipboard, drag+drop and shell dialogs operating correctly:
Thread ^ t = gcnew Thread(gcnew ThreadStart(this, &MainScreen::showModalDialog));
t->SetApartmentState(ApartmentState::STA);
t->Start();
And you generally have a Z-order problem with windows that are displayed on the main thread. With a nasty habit of your window disappearing behind a window owned by the main thread. No good solution for that.
The problem is showModalPopup uses Show method instead ShowDialog. The thread starts, shows the form (it doesnt block execution), starts the timer, ends and joins to the main thread. The thread where the form was created has been finished and it's here where the winforms multithreading issues comes.
Start the timer first and then show the modal window with ShowDialog. Something like this:
System::Void showModalPopup()
{
startTimer();
this->ShowDialog();
}

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.

Resources