0

So I was making an application using C++ Console, with multi threading as below, then I got an error 0x0000005.

The first time it run it was working as usual. Can anyone help me with this problem?

I am using Code::Blocks IDE with Borland C++ 5.5, and I am planning to make this into Borland C++ 5.02

#include <windows.h>
#include <stdio.h>
#include <dos.h>
#include <iostream.h>
#include <conio.h>

void linesmov(int mseconds, int y);

void linesmov(int mseconds, int y)
{
    int i=0;
    while (true)
    {
        i=i+1;
        // Or system("cls"); If you may...
        gotoxy(i,y);   
        cout << "____||____||____"; 
        gotoxy(i-1,y);
        cout << " ";
        Sleep(mseconds);
        if (i>115)
        {     
            i=0;  
            for(int o = 0; o < 100; o++)
            {
                gotoxy(0,y);   
                cout << "                  ";
            }
        }
    }
}

DWORD WINAPI mythread1(LPVOID lpParameter)
{
    printf("Thread inside %d \n", GetCurrentThreadId());
    linesmov(5,10);
    return 0;
}
DWORD WINAPI mythread2(LPVOID lpParameter)
{
    printf("Thread inside %d \n", GetCurrentThreadId());
    linesmov(30,15);
    return 0;
}

int main(int argc, char* argv[])
{
    HANDLE myhandle1;
    DWORD mythreadid1;
    HANDLE myhandle2;
    DWORD mythreadid2;
    myhandle1 = CreateThread(0,0,mythread1,0,0,&mythreadid1);
    myhandle2 = CreateThread(0,0,mythread2,0,0,&mythreadid2);
    printf("Thread after %d \n", mythreadid1);

    getchar();
    return 0;
}
Zombie Chibi XD
  • 370
  • 3
  • 17
  • I can't reproduce an error in MCVS15 using `void gotoxy(int x, int y) { COORD c = { x, y }; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), c); }` instead of borland specific `gotoxy`. May be `gotoxy` is the root of problem? – Smit Ycyken Mar 20 '18 at 09:21
  • 4
    Are you sure that `gotoxy` is thread-safe? – molbdnilo Mar 20 '18 at 09:22
  • Did your debugger tell you something? – Jabberwocky Mar 20 '18 at 09:25
  • you're not wating for threads to finish – Swift - Friday Pie Mar 20 '18 at 09:27
  • There's almost no way this is thread safe. You've got 2 different threads calling `gotoxy` to set the output location of text in the console - there's a classic race condition here. – Sean Mar 20 '18 at 09:27
  • It's using borland C++ 5.5 Compiler. It wont be able to be reproduced via MCVS15. gotoxy is not the root of the problem. I can use it in an infinite loop, so I am sure it's not the problem. @SmitYcyken – Zombie Chibi XD Mar 20 '18 at 10:44
  • @molbdnilo I don't know. How to make sure that gotoxy is thread safe? – Zombie Chibi XD Mar 20 '18 at 10:48
  • @Sean What do you mean by that? – Zombie Chibi XD Mar 20 '18 at 10:49
  • @SazeimSaheem - One thread can call `gotoxy` but before it does its ouput another thread may also call `gotoxy`, so the location of your output will be garbage. – Sean Mar 20 '18 at 10:55
  • @SazeimSaheem, just for the experiment, try to replace Borland `gotoxy` to that function I posted before. It only uses WINAPI. – Smit Ycyken Mar 20 '18 at 11:08
  • @SmitYcyken Ok. I just checked on your function, it was to me brilliant, but.. The error still occur... :/ – Zombie Chibi XD Mar 20 '18 at 11:20
  • @SmitYcyken Thank you for your function. After I did some experiments, like changing std::cout to printf(); and changing Sleep(); to sleep(); and using your function, I've been able to Eliminate the crash. The problem seems to be at std::cout, while using printf(); didn't cause the program to crash. Using the ordinary gotoxy will cause some funny bug in the program lol -> [Image](https://imgur.com/gallery/c2wC2) – Zombie Chibi XD Mar 20 '18 at 11:48

2 Answers2

1

All of these solutions in comments including mine are definitely not the way how it should be done. The main problem is lack of synchronization between threads and lack of processing their termination. Also, every function should be checked for thread-safe compatibility or should be wrapped to match it.

Considering std::cout since c++11 we have some data race guarantees:

Concurrent access to a synchronized (§27.5.3.4) standard iostream object’s formatted and unformatted input (§27.7.2.1) and output (§27.7.3.1) functions or a standard C stream by multiple threads shall not result in a data race (§1.10). [ Note: Users must still synchronize concurrent use of these objects and streams by multiple threads if they wish to avoid interleaved characters. — end note ]

So lask of synchronization primitives is oblivious according to this note.

Considering processing of thread termination.

HANDLE threadH = CreateThread(...);
...
TerminateThread(threadH, 0); // Terminates a thread.
WaitForSingleObject(threadH, INFINITE); // Waits until the specified object is in the signaled state or the time-out interval elapses.
CloseHandle(threadH); // Closes an open object handle.

TerminateThread(), but be aware of this solution, because ..

WaitForSingleObject()

And this is only first steps to thread-safe way.

I would like to recommend C++ Concurrency in Action: Practical Multithreading by Anthony Williams for further reading.

Rude solution for synchronized output

#include <Windows.h>
#include <iostream>
#include <mutex>

std::mutex _mtx; // global mutex

bool online = true; // or condition_variable

void gotoxy(int x, int y)
{
    COORD c = { x, y };
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), c);
}

void linesmov(int mseconds, int y) {
    int i = 0;
    while (online) {
        i = i + 1;
        // Or system("cls"); If you may...

        _mtx.lock(); // <- sync here
        gotoxy(i, y);
        std::cout << "____||____||____"; gotoxy(i - 1, y);
        std::cout << " ";
        _mtx.unlock();  

        Sleep(mseconds);
        if (i > 75)
        {
            i = 0;
            for (int o = 0; o < 60; o++)
            {
                _mtx.lock(); // <- sync here
                gotoxy(0, y);
                std::cout << "                  ";
                _mtx.unlock();
            }
        }
    }
}

DWORD WINAPI mythread1(LPVOID lpParameter)
{
    std::cout << "Thread 1" << GetCurrentThreadId() << std::endl;
    linesmov(5, 10);
    return 0;
}
DWORD WINAPI mythread2(LPVOID lpParameter)
{
    std::cout << "Thread 2" << GetCurrentThreadId() << std::endl;
    linesmov(30, 15);
    return 0;
}

int main(int argc, char* argv[])
{
    DWORD mythreadid1;
    DWORD mythreadid2;
    HANDLE myhandle1 = CreateThread(0, 0, mythread1, 0, 0, &mythreadid1);
    HANDLE myhandle2 = CreateThread(0, 0, mythread2, 0, 0, &mythreadid2);

    std::cout << "Base thread: " << GetCurrentThreadId() << std::endl;

    getchar();

    online = false;

    WaitForSingleObject(myhandle1, INFINITE);
    WaitForSingleObject(myhandle2, INFINITE);

    CloseHandle(myhandle1);
    CloseHandle(myhandle2);

    return 0;
}
Smit Ycyken
  • 1,189
  • 1
  • 11
  • 25
  • May I ask what is #include @Smit Ycyken – Zombie Chibi XD Mar 20 '18 at 13:33
  • [synchronization primitives - mutex](https://learn.microsoft.com/en-us/dotnet/standard/threading/mutexes), [std::mutex](http://en.cppreference.com/w/cpp/thread/mutex) – Smit Ycyken Mar 20 '18 at 13:34
  • At least this fixed the problem, but it's C++11, so I can't make it useable on older compilers... (T_T ) – Zombie Chibi XD Mar 20 '18 at 13:52
  • WINAPI [critical section object](https://msdn.microsoft.com/en-us/library/windows/desktop/ms682530(v=vs.85).aspx) provides synchronization similar to that provided by a mutex object – Smit Ycyken Mar 20 '18 at 13:58
  • Try to wrap it to the class and get rid of global variables, and it is better to use WINAPI `CreateEvent` to create an [event](https://stackoverflow.com/questions/3732015/will-using-createevent-to-create-open-an-even-that-already-exists-reset-the-sign) instead of `bool online` – Smit Ycyken Mar 20 '18 at 15:12
  • Also, it does not hurt to use the return value of WINAPI functions to check runtime errors and handle them. – Smit Ycyken Mar 20 '18 at 15:19
  • After I checked again, the point why I used multi threading is I want so make the animation move at a different speed. After I saw a few times, I found out that the animation was going on the same speed, then skipping to the actual place it supposed to be. Any ways to fix this? One hell of a problem I think (-_- ') – Zombie Chibi XD Mar 21 '18 at 02:24
0

a) Both gotoxy not outputting via std::cout are not thread safe /synchronized. You need process-wide mutex to synchronize that

b) exception is likely due to fact that you do not use WaitForMultipleObjects in main to wait for threads to finish. Depending on hardware and optimization main may exit before threads finish their work.

Swift - Friday Pie
  • 12,777
  • 2
  • 19
  • 42
  • Output to `cout` is safe. – molbdnilo Mar 20 '18 at 10:18
  • The problem was at using std::cout. Replacing it with printf has prevented the crash, but left with some buggy result with gotoxy, but the cause of the crash was std::cout. Anyways thanks! – Zombie Chibi XD Mar 20 '18 at 11:51
  • @molbdnilo Output to cout is NOT safe. – Zombie Chibi XD Mar 20 '18 at 11:51
  • @SazeimSaheem Yes it is, but if your program is undefined, changing anything at all may make a crash go away. – molbdnilo Mar 20 '18 at 12:08
  • @molbdnilo c++11 explicitly says that is possible to syncronize output but it is not so by default, yet without additional syncronization (see Smit's answer) you will still get garbage output. Before c++11 (and that's OPs case) there was no guarantee for that to be safe – Swift - Friday Pie Mar 20 '18 at 14:26