【问题标题】:0xC0000005 Error on Multi Thread Animation0xC0000005 多线程动画错误
【发布时间】:2018-03-20 09:11:36
【问题描述】:

所以我使用 C++ Console 制作了一个应用程序,具有如下多线程,然后我收到错误 0x0000005。

第一次运行时它像往常一样工作。谁能帮我解决这个问题?

我正在将 Code::Blocks IDE 与 Borland C++ 5.5 一起使用,并且我计划将其变成 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;
}

【问题讨论】:

  • 我无法使用 void gotoxy(int x, int y) { COORD c = { x, y }; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), c); } 而不是 borland 特定的 gotoxy 重现 MCVS15 中的错误。可能gotoxy是问题的根源?
  • 你确定gotoxy 是线程安全的吗?
  • 你的调试器告诉你什么了吗?
  • 你不会等待线程完成
  • 这几乎不可能是线程安全的。您有 2 个不同的线程调用 gotoxy 来设置控制台中文本的输出位置 - 这里有一个经典的竞争条件。

标签: c++ multithreading animation console-application borland-c++


【解决方案1】:

包括我在内的 cmets 中的所有这些解决方案绝对不是应该的方式。主要问题是线程之间缺乏同步以及缺乏处理它们的终止。此外,每个函数都应该检查线程安全的兼容性,或者应该被包装以匹配它。

考虑到std::cout,从 c++11 开始,我们有一些数据竞争保证:

并发访问同步(第 27.5.3.4 节)标准 iostream 对象的格式化和未格式化输入(§27.7.2.1)和输出 (§27.7.3.1) 函数或多线程的标准 C 流应 不会导致数据竞争(§1.10)。 [注:用户必须仍然 同步多个对象和流的并发使用 线程,如果他们希望避免交错字符。 ——尾注]

因此,根据这篇笔记,同步原语的 lask 是无意识的。

考虑线程终止的处理。

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(),但请注意此解决方案,because ..

WaitForSingleObject()

这只是线程安全方式的第一步。

我想推荐 Anthony Williams 的 C++ Concurrency in Action: Practical Multithreading 以供进一步阅读。

同步输出的粗鲁解决方案

#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;
}

【讨论】:

  • 请问什么是#include @Smit Ycyken
  • 至少这解决了这个问题,但它是 C++11,所以我不能让它在旧编译器上可用......(T_T)
  • WINAPI critical section object 提供类似于互斥对象提供的同步
  • 尝试将其包装到类中并去掉全局变量,最好使用WINAPICreateEvent创建event而不是bool online
【解决方案2】:

a) 两个不通过 std::cout 输出的 gotoxy 都不是线程安全/同步的。您需要进程范围的互斥锁来同步它

b) 异常可能是由于您没有在 main 中使用 WaitForMultipleObjects 来等待线程完成。根据硬件和优化,main 可能会在线程完成工作之前退出。

【讨论】:

  • 输出到cout 是安全的。
  • 问题在于使用 std::cout。用 printf 替换它可以防止崩溃,但是用 gotoxy 留下了一些错误的结果,但崩溃的原因是 std::cout。总之谢谢!
  • @molbdnilo 输出到 cout 不安全。
  • @SazeimSaheem 是的,但如果您的程序未定义,则更改任何内容都可能使崩溃消失。
  • @molbdnilo c++11 明确表示可以同步输出,但默认情况下并非如此,但如果没有额外的同步(参见 Smit 的回答),您仍然会得到垃圾输出。在 c++11(这是 OPs 案例)之前,不能保证它是安全的
猜你喜欢
  • 2021-12-17
  • 1970-01-01
  • 2020-02-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-13
  • 1970-01-01
  • 1970-01-01
  • 2014-08-20
相关资源
最近更新 更多