hellomyprogram 发表于 2023-8-3 16:27:08

C++ std::bad_function_call 与 Segmentation Fault 同时出现

本帖最后由 hellomyprogram 于 2023-8-4 07:15 编辑

在我的代码中,如果保留 threads.cpp 第 46 行的 if (function) ,那么我的程序会突然无响应。
而如果删除的话,我的代码会报 Segmentation Fault 和 std::bad_function_call ,但是它们出现似乎是随机的,有的时候有,有的时候没有。

Segmentation Fault 错误出现在 main.cpp 的第 17 行。 std::bad_function_call 出现在 threads.cpp 的第 45 行。
请问这是为什么?又怎么解决?

--- 已解决 ---
问题出自内存泄漏,在 DistributeThread::run 下调用了 sleep 函数,但是 sleep 函数只接受整数,因此我输入的小数都被整型化为 0 ,因此 DistributeThread “疯了”,乱发任务,导致内存泄漏,在崩溃时刻,线程里的任务队列已经积攒到百万数量级。

isdkz 发表于 2023-8-3 16:27:29

这种情况通常发生在多线程代码中,涉及了竞态条件(race condition)或者内存访问错误。Segmenation Fault 表示你的程序试图访问了无效的内存地址,而 std::bad_function_call 则表示你尝试调用一个空的 std::function 对象。

解决这个问题的步骤如下:

1. 调试 Segmentation Fault:
   · 确保你没有越界访问数组或者指针。
   · 确保没有在使用已释放的内存。
   · 确保没有使用空指针。
   · 使用工具如valgrind来检查内存错误。

2. 调试 std::bad_function_call:
   · 确保在调用 std::function 之前,该对象已经被正确地初始化。
   · 确保你没有在多个线程中同时修改或调用同一个 std::function 对象,可能需要使用互斥锁来保护。
   · 如果是从其他线程调用的 std::function,确保在调用之前该函数对象不会被销毁。

3. 处理竞态条件:
   · 使用互斥锁(mutex)来保护共享数据,确保每个线程在访问共享数据之前先锁定互斥锁,在访问完毕后解锁。
   · 考虑使用原子操作来进行简单的线程安全操作。

4. 使用调试工具:
   · 使用调试器(如 GDB)来逐步调试程序,查找问题所在。
   · 使用线程检测工具(如 Helgrind)来检测多线程代码中的问题。

5. 编写更健壮的代码:
   · 仔细设计多线程架构,避免竞态条件和死锁。
   · 使用智能指针等 RAII 技术来管理资源,避免内存泄漏。

在定位问题时,可以根据错误出现的条件和频率来排查。如果问题确实难以找到,可能需要更详细的代码和错误信息来进行深入分析。

学习编程中的Ben 发表于 2023-8-3 16:30:36

问题分析

根据提供的代码文件和问题描述,出现 Segmentation Fault 和 std::bad_function_call 错误的位置分别在 main.cpp 的第 17 行和 threads.cpp 的第 45 行。这些错误似乎是随机出现的。

由于没有提供完整的代码,我无法直接定位问题的具体原因。但是可以给出一些可能导致这些错误的常见原因和解决方法。

解决方案

1. Segmentation Fault 错误:
   - 检查第 17 行代码附近的指针操作,确保指针有效,并且没有被释放、空指针解引用等问题。
   - 检查是否存在数组越界的情况,确保数组访问不超过合法范围。
   - 使用合适的内存管理技术,如智能指针(std::shared_ptr、std::unique_ptr)来避免内存泄漏和悬挂指针等问题。
   - 如果有多线程代码,请确保在访问共享资源时正确地进行同步,以避免竞态条件等问题。

2. std::bad_function_call 错误:
   - 检查第 45 行代码附近的函数对象使用,确保在调用函数对象之前,已经将有效的函数对象绑定到它上面。
   - 在使用函数对象之前,可以使用条件语句或者std::function::target来检查函数对象是否为空,避免调用无效的函数对象。
   - 确保函数签名匹配,避免调用时参数类型不匹配或者返回值类型不一致。

3. 除了以上的常见问题,还可能存在其他与特定代码相关的问题。这种情况下,需要仔细检查相关代码,进行逐行调试并使用调试器来跟踪程序执行过程,以找出错误发生的具体原因。

请注意,由于我无法直接查看并修改你上传的代码,以上解决方案只是根据常见问题给出的一般性建议。如果你能提供更多关于问题的上下文信息或相关的代码片段,我可以尝试提供更具体的帮助。

如果回答对你有帮助,请给我一个最佳答案!{:10_254:}{:10_254:}{:10_254:}

hellomyprogram 发表于 2023-8-3 16:38:20

完整代码如下:
// main.cpp
#include <SFML/Graphics.hpp>
#include "threads.h"

DistributeThread threads;

class Main
{
        public:
                double view_direction = {0, 0};
                double player_position = {0, 0, 0};
                sf::RenderWindow *window = nullptr;
                sf::RenderWindow *window_log = nullptr;
                sf::Event event;

                void control()
                {
                        while (this->window->pollEvent(event))
                        {
                                switch (event.type)
                                {
                                        case sf::Event::KeyPressed:
                                                if (event.key.control)
                                                {
                                                        switch (event.key.code)
                                                        {
                                                                case sf::Keyboard::L:
                                                                        if (this->window_log == nullptr)
                                                                        {
                                                                                this->window_log->setVisible(true);
                                                                        }
                                                                        break;
                                                        };
                                                };
                                                break;
                                        case sf::Event::Closed:
                                                this->window->close();
                                                threads.stop();
                                                break;
                                };
                        };
                };

                void init()
                {
                        this->window = new sf::RenderWindow(sf::VideoMode(1000, 1000), "Minecraft for C");
                        this->window_log = new sf::RenderWindow(sf::VideoMode(1000, 1000), "Minecraft for C");
                        this->window_log->setVisible(false);
                };
};

int main()
{
        Main system;
        threads.work("Control", std::bind(Main::init, &system));
        threads.repeat_work("Control", 50, std::bind(Main::control, &system));
        if (threads.start())
        {
                threads.join();
        };
        return 0;
};
// math.cpp
#include <cmath>
#include "math.h"
#include "constants.hpp"

double radian(const double theta){
        return theta * (constants::pi / 180);
};

Matrix::Matrix(const Matrix &item) : Matrix(item.dimensions, item.values)
{};

Matrix::Matrix(Matrix &&item)
{
        this->dimensions = item.dimensions;
        this->dimensions = item.dimensions;
        this->values = item.values;       
        item.values = nullptr;
};

Matrix::Matrix(const unsigned short dimensions)
{
        this->dimensions = dimensions;
        this->dimensions = dimensions;
        this->values = new double * dimensions]();
};

Matrix::Matrix(const unsigned short dimensions, const double *values) : Matrix(dimensions)
{
        for (unsigned int ptr_offset = 0; ptr_offset < dimensions * dimensions; ptr_offset++)
        {
                *(this->values + ptr_offset) = *(values + ptr_offset);
        };
};

Matrix::~Matrix()
{
        if (this->values != nullptr)
        {
                delete[] this->values;
        };
};

Matrix Matrix::operator+(const Matrix &other) const
{
        if (this->dimensions != other.dimensions || this->dimensions != other.dimensions)
        {
                throw errors::MatrixDimensionError();
        };
        Matrix result(*this);
        for (unsigned int ptr_offset = 0; ptr_offset < this->dimensions * this->dimensions; ptr_offset++)
        {
                *(result.values + ptr_offset) += *(other.values + ptr_offset);
        };
        return result;
};

Matrix Matrix::operator+(const double other) const
{
        Matrix result(*this);
        for (unsigned int ptr_offset = 0; ptr_offset < this->dimensions * this->dimensions; ptr_offset++)
        {
                *(result.values + ptr_offset) += other;
        };
        return result;
};

Matrix Matrix::operator-() const
{
        Matrix result(*this);
        for (unsigned int ptr_offset = 0; ptr_offset < this->dimensions * this->dimensions; ptr_offset++)
        {
                *(result.values + ptr_offset) += -*(result.values + ptr_offset);
        };
        return result;
};

Matrix Matrix::operator-(const Matrix &other) const
{
        Matrix reversed = -other;
        return *this + reversed;
};

Matrix Matrix::operator-(const double other) const
{
        return *this + -other;
};

Matrix Matrix::operator*(const Matrix &other) const
{
        if (this->dimensions != other.dimensions)
        {
                throw errors::MatrixDimensionError();
        };
        unsigned short result_dimensions = {this->dimensions, other.dimensions};
        Matrix result(result_dimensions);
        int result_ptr_offset, this_ptr_offset, other_ptr_offset;
        for (short result_row = 0; result_row < result_dimensions; result_row++)
        {
                for (short result_column = 0; result_column < result_dimensions; result_column++)
                {
                        result_ptr_offset = result_row * result_dimensions + result_column;
                        for (short index = 0; index < this->dimensions; index++)
                        {
                                this_ptr_offset = result_row * this->dimensions + index;
                                other_ptr_offset = index * other.dimensions + result_column;
                                *(result.values + result_ptr_offset) += *(this->values + this_ptr_offset) * *(other.values + other_ptr_offset);
                        };
                };
        };
        return result;
};

Vector Matrix::operator*(const Vector &other) const
{
        unsigned short vector_matrix_dimensions = {other.dimensions, 1};
        Matrix vector_matrix(vector_matrix_dimensions, other.values);
        Matrix result_matrix = *this * vector_matrix;
        Vector result = Vector(result_matrix.dimensions, result_matrix.values);
        return result;
};

Matrix Matrix::operator*(const double other) const
{
        Matrix result(*this);
        for (unsigned int ptr_offset = 0; ptr_offset < this->dimensions * this->dimensions; ptr_offset++)
        {
                *(result.values + ptr_offset) *= other;
        };
        return result;
};

Matrix Matrix::operator/(const double other) const
{
        return *this * (1.0 / other);
};

Matrix Matrix::transpose() const
{
        Matrix result(this->dimensions);
        unsigned short row, column;
        for (unsigned int ptr_offset = 0; ptr_offset < this->dimensions * this->dimensions; ptr_offset++)
        {
                row = ptr_offset / this->dimensions;
                column = ptr_offset % this->dimensions;
                *(result.values + column + row * this->dimensions) = *(this->values + ptr_offset);
        };
        return result;
};

Vector::Vector(const Vector &item) : Vector(item.dimensions, item.values)
{};

Vector::Vector(const unsigned short dimensions, const double values[])
{
        this->dimensions = dimensions;
        this->values = new double();
        for (unsigned short index = 0; index < dimensions; index++)
        {
                this->values = values;
        };
};

Vector::Vector(Vector &&item)
{
        this->dimensions = item.dimensions;
        this->values = item.values;
        item.values = nullptr;
};

Vector::~Vector()
{
        if (this->values != nullptr)
        {
                delete[] this->values;
        };
};

Vector Vector::operator+(const Vector &other) const
{
        if (this->dimensions != other.dimensions)
        {
                throw errors::VectorDimensionError();
        };
        Vector result(*this);
        for (unsigned short index = 0; index < this->dimensions; index++)
        {
                result.values += other.values;
        };
        return result;
};

Vector Vector::operator+(const double other) const
{
        Vector result(*this);
        for (unsigned short index = 0; index < this->dimensions; index++)
        {
                result.values += other;
        };
        return result;
};

Vector Vector::operator-() const
{
        Vector result = *this;
        for (unsigned short index = 0; index < this->dimensions; index++)
        {
                result.values = -result.values;
        };
        return result;
};

Vector Vector::operator-(const Vector &other) const
{
        Vector reversed = -other;
        return *this + reversed;
};

Vector Vector::operator-(const double other) const
{
        return *this + -other;
};

Vector Vector::operator*(const Vector &other) const
{
        if (this->dimensions != 3 || other.dimensions != 3)
        {
                throw errors::VectorDimensionError();
        };
        Vector result(*this);
        for (unsigned short index = 0; index < this->dimensions; index++)
        {
                result.values = this->values[(index + 1) % 3] * other.values[(index + 2) % 3] - this->values[(index + 2) % 3] * other.values[(index + 1) % 3];
        };
        return result;
};

Vector Vector::operator*(const double other) const
{
        Vector result(*this);
        for (unsigned short index = 0; index < this->dimensions; index++)
        {
                result.values *= other;
        };
        return result;
};

Vector Vector::operator/(const double other) const
{
        return *this * (1.0 / other);
};

double Vector::dot(const Vector &other) const
{
        if (this->dimensions != other.dimensions)
        {
                throw errors::VectorDimensionError();
        };
        double result = 0;
        for (unsigned short index = 0; index < this->dimensions; index++)
        {
                result += this->values * other.values;
        };
        return result;
};

double Vector::length() const
{
        double result_squared = 0;
        for (unsigned short index = 0; index < this->dimensions; index++)
        {
                result_squared += this->values * this->values;
        };
        return sqrt(result_squared);
};

Vector Vector::length(const int scale) const
{
        return *this / (this->length() * scale);
};

double Vector::angle(const Vector &other) const
{
        return acos(this->dot(other) / (this->length() * other.length()));
};
// threads.cpp
#include <unistd.h>
#include <iostream>
#include "threads.h"

BasicThread::BasicThread(const char* name)
{
        this->name.assign(name);
        this->state = choices::state_free;
        this->thread = new std::thread(&BasicThread::run, this);
};

choices::state BasicThread::get_state()
{
        return this->state;
};

[]
std::unique_lock<std::mutex> * BasicThread::pause()
{
        this->state = choices::state_paused;
        std::unique_lock<std::mutex> *lock = new std::unique_lock<std::mutex>(this->mutex);
        return lock;
};

void BasicThread::resume(std::unique_lock<std::mutex> &lock)
{
        lock.unlock();
        this->alarm.notify_all();
};

void BasicThread::run()
{
        this->lock = new std::unique_lock<std::mutex>(this->mutex);
        this->lock->unlock();
        std::function<void ()> function;
        while (this->running)
        {
                this->state = choices::state_running;
                if (this->queue.empty())
                {
                        this->sleep();
                }
                else
                {
                        function = queue.front();
                        if (function)
                        function();
                        this->queue.pop();
                };
        };
        delete this->lock;
};

void BasicThread::sleep()
{
        this->state = choices::state_free;
        this->alarm.wait(*this->lock);
};

void BasicThread::stop()
{
        this->running = false;
        this->alarm.notify_all();
        this->thread->join();
        this->state = choices::state_stopped;
};

void BasicThread::work(std::function<void ()> function)
{
        this->queue.push(function);
        this->alarm.notify_all();
};

BasicThread * DistributeThread::create(const char* name)
{
        std::string name_string = name;
        if (this->threads.find(name_string) != this->threads.end())
        {
                throw errors::ThreadExistError();
        };
        BasicThread *new_thread = new BasicThread(name);
        this->threads = new_thread;
        this->locks = std::unique_lock<std::mutex>(new_thread->mutex);
        this->locks.unlock();
        return new_thread;
};

bool DistributeThread::start()
{
        if (this->plans.empty())
        {
                return false;
        };
        this->thread = new std::thread(DistributeThread::run, this);
        return true;
};

BasicThread * DistributeThread::get(const char *thread_name)
{
        std::string thread_string = thread_name;
        if (this->threads.find(thread_string) == this->threads.end())
        {
                return this->create(thread_name);
        }
        else
        {
                return this->threads;
        };
};

void DistributeThread::join()
{
        this->thread->join();
};

void DistributeThread::repeat_work(const char* thread_name, unsigned int delay, std::function<void ()> function)
{
        plan new_plan;
        new_plan.delay = delay;
        new_plan.function = function;
        new_plan.thread = this->get(thread_name);
        this->plans = new_plan;
        this->queue.push(queue_item(delay, last_id));
        last_id++;
};

void DistributeThread::run()
{
        queue_item function;
        plan function_plan;
        while (this->running)
        {
                function = this->queue.top();
                if (function.first > this->game_time)
                {
                        sleep((function.first - this->game_time) / 1000);// Change milliseconds into seconds
                        this->game_time = function.first;
                        continue;
                };
                this->queue.pop();
                function_plan = this->plans;
                function_plan.thread->work(function_plan.function);
                this->queue.push(queue_item(function.first + function_plan.delay, function.second));
        };
};

void DistributeThread::stop()
{
        this->running = false;
        for (auto iterator : this->threads)
        {
                iterator.second->stop();
        };
        this->thread->join();
};

void DistributeThread::work(const char* thread_name, std::function<void ()> function)
{
        this->get(thread_name)->work(function);
};
// math.h
#pragma once

double radian(const double);

class Vector
{
        public:
                unsigned short dimensions = 0;
                double *values = nullptr;

                Vector(const Vector &);
                Vector(Vector &&);
                Vector(const unsigned short, const double []);
                ~Vector();
                Vector operator+(const Vector &) const;
                Vector operator+(const double) const;
                Vector operator-() const;
                Vector operator-(const Vector &) const;
                Vector operator-(const double) const;
                Vector operator*(const Vector &) const;
                Vector operator*(const double) const;
                Vector operator/(const double) const;
                double dot(const Vector &) const;
                double length() const;
                Vector length(const int) const;
                double angle(const Vector &) const;
};

class Matrix
{
        public:
                unsigned short dimensions = {0, 0};
                double *values = nullptr; // "*" creates a ptr pointing at an array with two dimensions.

                Matrix(const Matrix &);
                Matrix(Matrix &&);
                Matrix(const unsigned short );
                Matrix(const unsigned short , const double *); // "*" is actually a ptr pointing at the first element of an array with two dimensions.
                ~Matrix();
                Matrix operator+(const Matrix &) const;
                Matrix operator+(const double) const;
                Matrix operator-() const;
                Matrix operator-(const Matrix &) const;
                Matrix operator-(const double) const;
                Matrix operator*(const Matrix &) const;
                Vector operator*(const Vector &) const;
                Matrix operator*(const double) const;
                Matrix operator/(const double) const;
                Matrix transpose() const;
};
// threads.h
#include <condition_variable>
#include <functional>
#include <thread>
#include <map>
#include <mutex>
#include <queue>
#include <unordered_map>
#include "constants.hpp"

class BasicThread
{
        friend class DistributeThread;
        public:
                std::string name = "Basic";
                BasicThread(const char*);
                choices::state get_state();
                std::unique_lock<std::mutex> * pause();
                void resume(std::unique_lock<std::mutex> &);
                void stop();
        protected:
                std::condition_variable alarm;
                std::unique_lock<std::mutex> *lock;
                std::mutex mutex;
                std::queue<std::function<void ()>> queue;
                bool running = true;
                choices::state state = choices::state_free;
                std::thread *thread = nullptr;
                void run();
                void sleep();
        public:
                void work(std::function<void ()> function);
};

class DistributeThread
{
        typedef std::pair<unsigned long long, unsigned int> queue_item;
        struct plan
        {
                unsigned int delay;
                std::function<void ()> function = nullptr;
                BasicThread* thread;
        };
        public:
                unsigned long long game_time = 0;
                std::string name = "Distributor";
                BasicThread * create(const char*);
                BasicThread * get(const char*);
                void join();
                void repeat_work(const char* thread_name, unsigned int delay, std::function<void ()> function);
                bool start();
                void stop();
                void work(const char* thread_name, std::function<void ()> function);
        protected:
                unsigned int last_id = 0;
                std::unordered_map<std::string, std::unique_lock<std::mutex>> locks;
                std::map<unsigned int, plan> plans;
                std::priority_queue<queue_item, std::vector<queue_item>, std::greater<queue_item>> queue;
                bool running = true;
                std::unordered_map<std::string, BasicThread*> threads;
                std::thread *thread = nullptr;
                void run();
};
页: [1]
查看完整版本: C++ std::bad_function_call 与 Segmentation Fault 同时出现